PHP radom array values from another array - php

///PROBLEM SOLVED. SEE MY ANSWER BELLOW.///
I have an array that randomly generates TRUE values throughout it. I
specified the number of TRUE values i want and it works like a charm.
I was toying around with it`s values and generating certain actions
based on wether or not the value is TRUE. Problem is i need to do this
again, this time using the TRUE values to form an array from which i
need a certain number of TRUE values.
I.E. I wanted 3 TRUE values out of 5. The code gave me the 3 TRUE
values on random iterations:
[1]=>1; [1]=>;
[2]=>1; [2]=>1;
[3]=>; [3]=>1;
[4]=>; [4]=>;
[5]=>1; [5]=>1;
This is all fine and dandy. Now i need TRUE values on 2 out of the 3
previous values. Consider i am taking 5 bites out of an apple. On 3
occasions i choke on it, from which in 2 cases i lose 2,3 teeth.
I want to print the outcome.
"Took a bite"
"Took a bite and choked"
"Took a bite"
"Took a bite , choked , lost 1 tooth and got 9 left" (i have a total of 10 teeth)
"Took a bite , choked , lost 2 teeth and got 7 left"
This is what i need to see printed after the 3/5 and 2/3 random
calculations occured. If i took a bite on the [2],[4] and [5]
iterations, i must lose tooth 2 times randomly on those exact
iterations.
Sorry for this example.
///PROBLEM SOLVED. SEE MY ANSWER BELLOW.///
2) Lastly, how can i store what happened after the code ran? Like "After you ate that apple, you choked 3 times lost 3 teeth and still got 7 left".
The way i did it does not work and returns "me loosing 1 tooth and having 9 left despite the fact that my calculated value is 7", not stacking the values of my ordeal.
I know these questions are silly, but i searched everywhere for information, read manuals and stuff and cannot put the pieces together...
The method used in the " random 3/5" case:
$spots = array();
while (count($spots) < number) {
$rand = rand(1,36);
if (!isset($spots[$rand])) {
$spots[$rand] = TRUE;
}
}
$spots = $spots + array_fill(1, number, FALSE);
ksort($spots); /// credits to Nick J.
Then did a foreach($spots as $k => $v)
Then did my code statements, starting with if ($v == 1), do code...
And gave me a random 3/5 list like the one i posted first.
Thank you in advance,
Vlad

Ok, i edited my answer as i have found the solution to problem number 1.
The catch was to scan the array of elements that are FALSE, unset them, work with that further on as it will randomly select elements from the TRUE elements of the original array.
Hope this comes in handy. I will post the code here:
$spots = array();
while (count($spots) < number) {
$rand = rand(1,constant);
if (!isset($spots[$rand])) {
$spots[$rand] = TRUE;
}
}
$spots = $spots + array_fill(1, constant, FALSE);
ksort($spots);
print_r($spots) ;
foreach($spots as $v)
{
if($v != 1)
{
unset($spots[array_search($v,$spots)]);
}
}
print_r($spots);
If example array:
$spots = array("1","2","3","4","5");
And i want 3 random numbers to be picked (TRUE = 1 , FALSE = nothing)
then the output is:
[1]=>1;
[2]=>1;
[3]=>;
[4]=>;
[5]=>1;
If i want 2 random numbers out of the 3 just got, then the final output is:
[1]=>1;
[2]=>1;
[5]=>1;

Related

Algorithm that generates all versions of an array with possible weights assigned 0-100

I have a following array (php):
[
[id=>1,weight=]
[id=>2,weight=]
[id=>3,weight=]
[id=>4,weight=]
]
I need to create all possible versions of this array asigning 0-100 weight to each item['weight'] with a step of N.
I don't know how this type of problems are called. It is NOT permutation/combination.
Lets say N is 10, I am aiming to get:
[
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=10]
[id=>4,weight=70]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=20]
[id=>4,weight=60]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=30]
[id=>4,weight=50]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=40]
[id=>4,weight=40]
]
...all possible combination of weights for id=x.
[
[id=>1,weight=70]
[id=>2,weight=10]
[id=>3,weight=10]
[id=>4,weight=10]
]
]
Sum of 4 item['weights'] in array on same level is always 100 (or 0.1). And inside parent array I've all possible combinations of weights from 10-100 for id=x.
This problem is sometimes described as allocating identical balls into distinct bins. You didn't specify your problem exactly, so I'll take a guess here but the logic will be identical.
I'll assume you're distributing b = N/step balls into 4 bins.
Think of the balls all in a row, and then using 3 bars to separate the balls into 4 bins:
*|||*****.
If N=10 and you're distributing 100 points, the above example is the same is 30, 20, 0, 50. If zeroes aren't allowed, you can reduce the amount you're distributing by 4*b and assume each bin starts out with N/step in it (so you're distributing the leftover points).
The number of ways to do this is choose(balls + bins - 1, bins - 1).
Theres probably a better way, but heres my attempt:
$result=array(); // Empty array for your result
$array=range(1117,7777); // Make an array with every number between 1117 and 7777
foreach ($array as $k=>$v) { // Loop through numbers
if ((preg_match('/[890]/',$v) === 0) && (array_sum(str_split($v, 1)) === 10)) {
// If number does not contain 8,9 or 0 and sum of all 4 numbers is 10
// Apply function to multiply each number by 10 and add to result array
$result[] = array_map("magnitude", str_split($v, 1));
}
}
function magnitude($val) { // function to multiply by 10 for array map
return($val * 10);
}
print_r($result);
Working demo here
EDIT
Sorry I realised my code explanation isn't totally clear and I condensed it all a bit too much to make it easy to follow.
In your example the first array would contain (10,10,10,70). For the sake of simplicity I divided everything by 10 for the calculations and then just multiplied by 10 once I had a result, so your array of (10,10,10,70) becomes (1,1,1,7). Then your final array would be (70,10,10,10) which would become (7,1,1,1).
My approach was to first to create an array containing every combination of these four numbers, which I did in two steps.
This line $array=range(1117,7777); creates an array like this (1117, 1118, 1119 ... 7775, 7776, 7777) (My number range should really have been 1117 - 7111 instead of 1117-7777).
Applying str_split($v, 1) to each value in the loop splits each 4 digit number in the array into another array conatining 4 single digit numbers, so 1117 will become (1, 1, 1, 7) etc
As each of your items can't have a weight below 10 or above 70 we use (preg_match('/[890]/',$v) === 0) to skip any arrays which have 0,8 or 9 in them anywhere, then array_sum(str_split($v, 1)) === 10) adds up the four digits in the array and only returns arrays which total 10 (you wanted ones which total 100, but I divided by 10 earlier).
array_map applies a function to each element in an array. In my example the function multiplies each value by 10, to undo the fact I divided by 10 earlier.
When you say is it possible to alter steps, can you give me a couple of examples of other values and the output you want for them?
If you want a totally different approach and using mysql isn't a problem then this also works:
Create a new table with a single row. Insert all the values you need to check
INSERT INTO `numbers` (`number`) VALUES
(10),
(20),
(30),
(40),
(50),
(60),
(70);
Then your php looks like this
$result=array();
try {
$dbh = new PDO('mysql:host=aaaaa;dbname=bbb', 'ccc', 'dddd');
foreach($dbh->query('SELECT *
FROM numbers a
CROSS JOIN // A cross join returns the cartesian product of rows
numbers b // so every row with every combination of the other rows
CROSS JOIN
numbers c
CROSS JOIN
numbers d
ON
a.number = b.number OR a.number != b.number') as $row) {
if (($row[0] + $row[1] + $row[2] + $row[3]) === 100) {
$result[] = $row;
}
}
$dbh = null;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
print_r($result);

Cartesian product with specific criteria

I am attempting to find the cartesian product and append specific criteria.
I have four pools of 25 people each. Each person has a score and a price. Each person in each pool looks as such.
[0] => array(
"name" => "jacob",
"price" => 15,
"score" => 100
),
[1] => array(
"name" => "daniel",
"price" => 22,
"score" => 200
)
I want to find the best combination of people, with one person being picked from each pool. However, there is a ceiling price where no grouping can exceed a certain price.
I have been messing with cartesians and permutation functions and cannot seem to figure out how to do this. The only way I know how to code it is to have nested foreach loops, but that is incredibly taxing.
This code below, as you can see, is incredibly inefficient. Especially if the pools increase!
foreach($poolA as $vA) {
foreach($poolb as $vB) {
foreach($poolC as $vC) {
foreach($poolD as $vD) {
// calculate total price and check if valid
// calculate total score and check if greatest
// if so, add to $greatest array
}
}
}
}
I also thought I could find a way to calculate the total price/score ratio and use that to my advantage, but I don't know what I'm missing.
As pointed out by Barmar, sorting the people in each pool allows you to halt the loops early when the total price exceeds the limit and hence reduces the number of cases you need to check. However, the asymptotic complexity for applying this improvement is still O(n4) (where n is the number of people in a pool).
I will outline an alternative approach with better asymptotic complexity as follow:
Construct a pool X that contains all pairs of people with one from pool A and the other from pool B.
Construct a pool Y that contains all pairs of people with one from pool C and the other from pool D.
Sort the pairs in pool X by total price. Then for any pairs with the same price, retain the one with the highest score and discard the remaining pairs.
Sort the pairs in pool Y by total price. Then for any pairs with the same price, retain the one with the highest score and discard the remaining pairs.
Do a loop with two pointers to check over all possible combinations that satisfy the price constraint, where the head pointer starts at the first item in pool X, and the tail pointer starts at the last item in pool Y. Sample code is given below to illustrate how this loop works:
==========================================================================
$head = 0;
$tail = sizeof($poolY) - 1;
while ($head < sizeof($poolX) && $tail >= 0) {
$total_price = $poolX[$head].price + $poolY[$tail].price;
// Your logic goes here...
if ($total_price > $price_limit) {
$tail--;
} else if ($total_price < $price_limit) {
$head++;
} else {
$head++;
$tail--;
}
}
for ($i = $head; $i < sizeof($poolX); $i++) {
// Your logic goes here...
}
for ($i = $tail; $i >= 0; $i--) {
// Your logic goes here...
}
==========================================================================
The complexity of steps 1 and 2 are O(n2), and the complexity of steps 3 and 4 can be done in O(n2 log(n)) using balanced binary tree. And step 5 is essentially a linear scan over n2 items, so the complexity is also O(n2). Therefore the overall complexity of this approach is O(n2 log(n)).
A couple of things to note about your approach here. Speaking strictly from a mathematics perspective, you're calculating way more permutations than is actually necessary to arrive at a definitive answer.
In combinatorics, there are two important questions to ask in order to arrive at the exact number of permutations necessary to yield all possible combinations.
Does order matter? (for your case, it does not)
Is repetition allowed? (for your case, it is not necessary to repeat)
Since the answer to both of these question is no, you need only a fraction of the iterations you're currently doing with your nested loop. Currently you are doing, pow(25, 4) permutations, which is 390625. You only actually need n! / r! (n-r)! or gmp_fact(25) / (gmp_fact(4) * gmp_fact(25 - 4)) which is only 12650 total permutations needed.
Here's a simple example of a function that produces combinations without repetition (and where order does not matter), using a generator in PHP (taken from this SO answer).
function comb($m, $a) {
if (!$m) {
yield [];
return;
}
if (!$a) {
return;
}
$h = $a[0];
$t = array_slice($a, 1);
foreach(comb($m - 1, $t) as $c)
yield array_merge([$h], $c);
foreach(comb($m, $t) as $c)
yield $c;
}
$a = range(1,25); // 25 people in each pool
$n = 4; // 4 pools
foreach(comb($n, $a) as $i => $c) {
echo $i, ": ", array_sum($c), "\n";
}
It would be pretty easy to modify the generator function to check whether the sum of prices meets/exceeds the desired threshhold and only return valid results from there (i.e. abandoning early where needed).
The reason repetition and order are not important here for your use case, is because it doesn't matter whether you add $price1 + $price2 or $price2 + $price1, the result will undoubtedly be the same in both permutations. So you only need to add up each unique set once to ascertain all possible sums.
Similar to chiwangs solutions, you may eliminate up front every group member, where another group member in that group exists, with same or higher score for a lower price.
Maybe you can eliminate many members in each group with this approach.
You may then either use this technique, to build two pairs and repeat the filtering (eliminate pairs, where anothr pair exists, with higher score for the same or lower costs) and then combine the pairs the same way, or add a member step by step (one pair, a triple, a quartett).
If there exists some member, who exceed the allowed sum price on their own, they can be eliminated up front.
If you order the 4 groups by score descending, and you find a solution abcd, where the sum price is legal, you found the optimal solution for a given set of abc.
The reponses here helped me figure out the best way for me to do this.
I haven't optimized the function yet, but essentially I looped through each results two at a time to find the combined salaries / scores for each combination in the two pools.
I stored the combined salary -> score combination in a new array, and if the salary already existed, I'd compare scores and remove the lower one.
$results = array();
foreach($poolA as $A) {
foreach($poolB as $B) {
$total_salary = $A['Salary'] + $B['Salary'];
$total_score = $A['Score'] + $B['Score'];
$pids = array($A['pid'], $B['pid']);
if(isset($results[$total_salary]) {
if($total_score > $results[$total_salary]['Score']) {
$results[$total_salary]['Score'] => $total_score;
$results[$total_salary]['pid'] => $pids;
} else {
$results[$total_salary]['Score'] = $total_score;
$results[$total_salary]['pid'] = $pids;
}
}
}
After this loop, I have another one that is identical, except my foreach loops are between $results and $poolC.
foreach($results as $R) {
foreach($poolC as $C) {
and finally, I do it one last time for $poolD.
I am working on optimizing the code by putting all four foreach loops into one.
Thank you everyone for your help, I was able to loop through 9 lists with 25+ people in each and find the best result in an incredibly quick processing time!

Pair all elements of an array in all possible combinations php

I've done some searching all i could find is this unfortunately thats not exactly what i'm looking for.
My problem is, i have a list of user ids in an array that could vary in size from 0 to 30+ users.
looks somehting like:
$arr = array(1,2,3);
What i need is to be able to find all pairs possible with this users:
(1 - 2)
(1 - 3)
(2 - 1)
(2 - 3)
(3 - 1)
(3 - 2)
the idea is to be able to create contact lists on a messaging platform so each one has each other on their contact list.
the examples on the question linked above gives me repeating elements and i can't have repeating elements also don't need 3 elements pared together only 2.
You have two objectives from the way I see it:
Display all possible pairs between a and b numbers
Don't display the same combination twice
You can achieve this with two loops and by keeping track of what you've processed already:
$arr = range(1, 30);
$alreadyProcessed = array();
foreach ($arr as $first) {
// Loop the array twice (as #Dagon mentioned)
foreach ($arr as $second) {
// Keep track of what you've already processed
$combination = array($first, $second);
// Sorting the numbers will ensure that 2 - 1 and 1 - 2 are the same
sort($combination);
// Ensure they aren't the same number and you haven't already processed them
if ($first === $second || in_array($combination, $alreadyProcessed)) {
continue;
}
// Output as per your example
echo "($first - $second)" . PHP_EOL;
// Add it to the list of what you've already processed
$alreadyProcessed[] = $combination;
}
}
NOTE: this kind of logic is bad. You're performing an exponential number of repetitions because you're looping an array inside a loop of the same array. This particular example will perform 30 to-the-power-of 30 repetitions (a big, big number of cycles). That's only with 30 entries. What happens if your user base grows to 10,000? Bye bye server.

Finding out what numbers have been added to come up with a sum

Sorry for the title. I wasn't sure how to ask this question.
I have a form on a website that asks a question. The answers are in check box form. Each answer is saved into my database with a 'score', the values look like this:
Allergy 1
Cardiology 2
Chest Disease 4
Dermatology 8
Emergency Room 16
Ambulance Trips 32
Gastroenterology 64
General Medicine 128
Gynecology 256
Hematology 512
Neurology 1024
Obstetrics 2048
Opthamology 4096
Orthopedics 8192
Physical Therapy 16384
Plastic Surgery 32768
Podiatry 65536
Proctology 131072
Psychiatry 262144
Surgery Performed 524288
Thoracic Surgery 1048576
Urology 2097152
Outside X-Rays 4194304
Diagnostic Tests (outside) 8388608
As you can see, the score is the previous value times two. When a user fills out the form, the answer is saved in the database as one value - all the answers added together.
For example, a user selected the values: Allergy, General Medicine, Hematology, Obstetrics. In the database, the answer for this question is saved as 2689.
Is there a way to figure out what answers have been selected by only having the answer to the question?
For example, I would query my database and pull the 2689 value, and I need to determine what answers were checked.
edit: I was hoping to reverse engineer the answer in PHP.
Yes, this is a common pattern called bit masking. Use your language's binary AND operator on the value corresponding to a given answer and the value submitted from the form to see if the given answer was one of the selected choices. For example, if the answer submitted and saved is 2689 as in your example, you can check whether "chest disease" was one of the selected choices by seeing if 2689 & 4 is nonzero. (& should be substituted with whatever the binary AND operator is in your language of choice.)
Note that this only works as long as all the values corresponding to individual choices are powers of 2. In general, the question posed in your title, about finding out what numbers from a given set have been added to come up with a given sum, is an instance of something called the knapsack problem and is only known to be solvable by checking every possible combination, which is very inefficient. (NP-complete, specifically)
You can find the values by ANDing with powers of 2.
20 = 1
21 = 2
22 = 4
23 = 8
...
223 = 8388608
You can find out the value of 2n using binary shifting like this: 1 << n
php like code:
$item[] = {"Allergy", "Cardiology", ..., "Diagnostic Tests (outside)"};
$answer = 2689;
for ( $power = 0; $power < count($item); $power++ ) {
if ( 1 << $power & $answer ) {
echo $item[$power] . "\n";
}
}
Edit: made it more php friendly
Yes, there is. Note that each k'th "score" is of the form 2^(k - 1), which corresponds to a bitstring with only the k'th bit set. If you know which bits are set, you can reconstruct the sum.
Taking 2689 as an example, we first need to write it out in binary:
2689 = 101010000001b
Counting from the right, we see that the first, eighth, tenth and twelfth bits are set, so (as you can verify)
2689 = 2^0 + 2^7 + 2^9 + 2^11
= 1 + 128 + 512 + 2048
The actual implementation of this can be done efficiently using bitwise operations. By taking the AND of the value and each of the "scores" in turn, then checking whether that gives a non-zero value, we can check which scores went into the sum.
this will do exactly what you wanted :
<?php
Print bindecValues("2689");
function bindecValues($decimal, $reverse=false, $inverse=false) {
$bin = decbin($decimal);
if ($inverse) {
$bin = str_replace("0", "x", $bin);
$bin = str_replace("1", "0", $bin);
$bin = str_replace("x", "1", $bin);
}
$total = strlen($bin);
$stock = array();
for ($i = 0; $i < $total; $i++) {
if ($bin{$i} != 0) {
$bin_2 = str_pad($bin{$i}, $total - $i, 0);
array_push($stock, bindec($bin_2));
}
}
$reverse ? rsort($stock):sort($stock);
return implode(", ", $stock);
}
?>
Happy coding
Remember that integers are stored in binary - so each of these flags (Allergy = 1) etc. will correspond to a single bit being true or false in the binary representation of the sum.
For example, 2689 in binary is 0000 1010 1000 0001 which, if you think of it as an array of bits, where the least significant bit (right most in that array) is the least significant flag (allergy) then we can easily see that the first (allergy), eighth (gen. medicine), tenth (hematology) and twelfth (obs) slots of the array are marked with a 1 for true.
The largest value in your array of flags is 24th bit in a 32 bit integer. You could define up to 8 more flags in this system before having to use a larger integer.
Since all your numbers seem to be powers of two, you just need to store the input value in a long enough integer to hold it, then bit mask.
if( value & 1 ) then 1 was part of the selection
if( value & 2 ) then 2 was part of the selection
if( value & 3 ) then 3 was part of the selection
and so on

Unidentified Error in Program: Unfriendly Numbers

There is a problem in Interview Street challange. Maybe the most easiest of all challenges. "Unfriendly Numbers", is the name and question goes like this.
There is one friendly number and N unfriendly numbers. We want to find how many numbers are there which exactly divide the friendly number, but does not divide any of the unfriendly numbers.
Input Format:
The first line of input contains two numbers N and K seperated by spaces. N is the number of unfriendly numbers, K is the friendly number.
The second line of input contains N space separated unfriendly numbers.
Output Format:
Output the answer in a single line.
I did a PHP programming like this:
<?php
/* Enter your code here. Read input from STDIN. Print output to STDOUT */
$handle = fopen ("php://stdin","r");
$input = fgets($handle);
$num_unfriendly_number=substr($input,0,1);
$friendly_number=substr($input,2,1);
$input2=fgets($handle);
for($i=0;$i<=($num_unfriendly_number); $i=$i+2){
$unfriendly_numbers[$i]=substr($input2,$i,1);
}
//truncates additional input
//now getting divisiors of given friendly numbers
$check_num=1;
//one is always a divisor of any number
$divisior[0]=1;
$arrayindex=1;
for($check_num; $check_num<=$friendly_number; $check_num++){
$hold_var=$friendly_number%$check_num;
if($hold_var==0){
$divisor[$arrayindex]=$check_num;
$arrayindex++;
}
}
$index=0;
foreach($divisor as $test_div){
$output=true;
foreach($unfriendly_numbers as $test_unfrnd){
if($test_unfrnd%$test_div){
$output=false;
}
}
if ($output){
$outputarray[$index]=$test_div;
$index++; //edited afterwards after #Boris's suggestion but didn't work :(
}
}
$num_of_output=count($outputarray);
define('STDOUT',fopen("php://stout","r"));
fwrite(STDOUT,$num_of_output);
?>
The above programme worked fine for 2 testcases but did not applied for other tests. I did some research but did not found any errors. Any helps please. Thanks in advance.
Fist of all I would like to mention that I do not know php. However, I think this is simple enough I can try to help.
Several errors I see:
for($i=0;$i<=($num_unfriendly_number); $i=$i+2){
$unfriendly_numbers[$i]=substr($input2,$i,1);
}
Here you use substr($input2,$i,1);, this however assumes all your unfriendly numbers are digits, which might not always be the case. Better use the split function in php. Replace the whole while with the following:
$unfriendly_numbers = explode(" ", $input2);
After that:
$index=0;
foreach($divisor as $test_div){
$output=true;
foreach($unfriendly_numbers as $test_unfrnd){
if($test_unfrnd%$test_div){
$output=false;
}
}
if ($output){
$outputarray[$index]=$test_div;
}
}
Here you never increase the $index variable. Isn't this meaning that you will override the divisors one with other? USe the operator []=. It appends to an array in php:
if ($output){
$outputarray []= $test_div;
}
EDIT One more error I see is that you count on the friendly number to be a digit too. You can fix this too:
$friendly_number=substr($input,2,1);
->
$friendly_number=explode(" ", $input)[0];
I have the same problem I can't understand why this code can't finish in less than 16 seconds!
I would like to hear your tricks
a = raw_input()# this will read this line: 8 16
b = raw_input()# this will read this line: 2 5 7 4 3 8 3 18
al = a.split()
bl = b.split()
blint = []
fn = int(al[1])
fnlist = [fn]
half_fn = fn / 2 # only I go to half the number to save some time
k = 1
while k <= half_fn:
if fn % k == 0:
fnlist.append(k)
k += 1
plist = []
for j in bl:
blint.append(int(j)) # here I changed the bl list elements which are string to int
for i in fnlist:
for j in blint: #I have the int elements so I don't need every time bring the string and change it to int
if j % i == 0:
plist.append(i)
break
counter = len(fnlist) - len(plist)
print counter

Categories