Related
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);
I need to generate a ten digits product code (for bar code generation) for a fashion store, which is a combination of:
collectionYear (this or next year), two digits, e.g. 14 or 15
collectionSeason (spring to winter) one digit, e.g. 1 for spring
productId (from 1 to 99.999 with zeros filled), e.g. 12 -> 00012
productVariant (from 1 to 99 with zero filled), e.g. 2 -> 02
The result should be something like this: 1510001202
Using php I would do this like this:
$year = str_pad($year, 2 ,'0', STR_PAD_LEFT); // for future safety, e.g. 2102 :-)
$prodId = str_pad($prodId, 5 ,'0', STR_PAD_LEFT);
$prodVar = str_pad($prodVar, 2 ,'0', STR_PAD_LEFT);
$finalCode = $year.$season.$prodId.$prodVar;
Is there a mysql syntax equiv for this like
SELECT CONCAT(collectionYear,collectionSeason,productIdproductVariant) AS prodCode
with the zero fill functions included ??
Thanks for your engagement.
$array = array(1, 2, 3, 4, 5, ..., N);
Also there is a number D = 10%. What is the fastest way to sort the array in such way that:
$sorted_array = {a[i]}
contains exactly the elements of $array in a mixed order, but also:
abs(a[i + 1] - a[i]) >= N * 10%
for any [i] and look randomized as much as possible.
For example,
// assume D = 25%
$array = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// so the difference between any neighbors is >= 4 = 10 * 25%.
$sorted_array = array(4, 8, 3, 7, 1, 5, 9, 2, 6, 10);
Of course if D is large, it is impossible to sort the array I want. I don't need the 100% perfect result, but I want the numbers to look "randomized" and most of them to be different at least for 10%.
I have a strange task but it has a practical area to use. I want to extract randomized lines from the image and they should be different as much as possible. Of course, the neighbor lines on the digital images (photos etc) look very similar.
Did I explain it properly?
I know it's not a good idea just to provide code, but I was intrigued by this question. Here's how I would do it:
$d = 0.3;
$random = array();
// Populate the original array
for ($n=1; $n <= 10; $n++) {
$arr[] = $n;
}
$count = count($arr);
// Loop through array
foreach (array_keys($arr) as $key) {
if (!isset($prev_key)) {
$prev_key = array_rand($arr);
}
$possibles = array(); // This stores the possible values
echo "Trying: $prev_key";
echo ":\n";
// Loop through the array again and populate $possibles with all possible
// values based on the previous values
foreach (array_keys($arr) as $n) {
if ($arr[$n] < $prev_key - $count * $d || $arr[$n] > $prev_key + $count * $d) {
$possibles[] = $n;
echo $arr[$n]." is valid\n";
}
else {
echo $arr[$n];
echo " outside range\n";
}
}
// If there is nothing outside that range, just return the remaining values
if (count($possibles) == 0) {
$possibles = array_keys($arr);
echo "Nothing within range so just returning whole array\n";
}
echo "\n";
// Choose random value from the possible values array
$rand_key = $possibles[array_rand($possibles)];
$random[] = $arr[$rand_key];
$prev_key = $arr[$rand_key];
// Unset this value from the original array since we can only use the
// values once
unset($arr[$rand_key]);
}
print_r($random);
This will produce output like this:
Trying: 8:
1 is valid
2 is valid
3 is valid
4 is valid
5 outside range
6 outside range
7 outside range
8 outside range
9 outside range
10 outside range
Trying: 2:
1 outside range
3 outside range
4 outside range
5 outside range
6 is valid
7 is valid
8 is valid
9 is valid
10 is valid
Trying: 9:
1 is valid
3 is valid
4 is valid
5 is valid
6 outside range
7 outside range
8 outside range
10 outside range
Trying: 5:
1 is valid
3 outside range
4 outside range
6 outside range
7 outside range
8 outside range
10 is valid
Trying: 10:
1 is valid
3 is valid
4 is valid
6 is valid
7 outside range
8 outside range
Trying: 4:
1 outside range
3 outside range
6 outside range
7 outside range
8 is valid
Trying: 8:
1 is valid
3 is valid
6 outside range
7 outside range
Trying: 3:
1 outside range
6 outside range
7 is valid
Trying: 7:
1 is valid
6 outside range
Trying: 1:
6 is valid
Array
(
[0] => 2
[1] => 9
[2] => 5
[3] => 10
[4] => 4
[5] => 8
[6] => 3
[7] => 7
[8] => 1
[9] => 6
)
The only drawback is that since it randomly gets rows, there is a chance that the values near the end may not be outside the defined range. By my tests, this happens to about 4% using the above $d = 0.25 and 1000 values. One way to get around this is just to insert these values back in at random places instead of appending them like I have done.
Also note, this method is not that efficient. It has to loop through the array count($arr) ^ 2 times. So for 1000 values, you're looking at 1,000,000 iterations. Fortunately the array gets progressively smaller.
What I need is to create five random integer (say rand(1,5)). Then, I generate a score based on these numbers. For instance, if I get a result of 1,2,3,4,5 then that would equal a zero score, but if I got 1,1,3,4,5 that would be 1 as we have a pair. Similar to a poker kind of scoring, so five of the same number would be a "full house" thus resulting in the highest score.
How would I go about the scoring system, even if it is just the mathematical equation?
More detail:
1-5 will hold separate images and then will be fought against "The House" which will have identical code to the user to determine the winner. Here's some example draws and the score they would receive:
1,2,3,4,5 = score 0
1,1,2,3,4 = score 1 (1 pair)
1,1,2,2,4 = score 2 (2 pair)
1,1,1,3,4 = score 3 (3 of a kind)
1,1,1,1,5 = score 4 (4 of a kind)
1,1,1,3,3 = score 5 (full house)
1,1,1,1,1 = score 6 (5 of a kind)
The combination of numbers is irreverent if they score 6 and the house scores 6, it's a tie.
if (isset($_POST['play'])) {
$rand1 = rand(1, 5);
$rand2 = rand(1, 5);
$rand3 = rand(1, 5);
$rand4 = rand(1, 5);
$rand5 = rand(1, 5);
if ($_POST['bet'] <= $user_data['coins']) {
if ($_POST['bet'] < 999999999) {
if ($_POST['bet'] > 0.99) {
if ($user_data['coins'] >= 1) {
$array = array($rand1,$rand2,$rand3,$rand4,$rand5);
print_r(array_count_values($array));
echo $rand1.', '.$rand2.', '.$rand3.', '.$rand4.', '.$rand5;
Array( // Here I don't understand
1 => 3,//
2 => 1,//
3 => 1 //
);
}
}
}
}
}
This outputs ; Array ( [5] => 2 [4] => 2 [1] => 1 ) 5, 5, 4, 4, 1
Use array_count_value function for this.
$array = array(1,1,1,2,5);
print_r(array_count_values($array));
Array(
1 => 3,
2 => 1,
3 => 1
);
Here's the approach I would consider, building on #Lele's answer. Warning: this is a bit confusing, so sit down with a cup of tea for this one.
Build a set of five buckets, [1] to [5], and scan a player's numbers, so that the count for each number is stored in the corresponding bucket
Then count the numbers you are left with into a new bucket system, with each position representing the number of counts you have for something.
So, if your score is this:
1 1 2 2 4
Then your first buckets are:
2 2 0 1 0
That's because you have two ones, two twos, and one four. And your second buckets are:
1 2 0 0 0
That's because you have two two-counts, and one one-count. Here, you disregard the first position (since a one-count for something does not score anything) and score for the others. So, test for two twos, and score that two.
If you score is this:
5 5 5 5 1
Then your first buckets are:
1 0 0 0 4
That's one one and four fives. So your second buckets are:
1 0 0 1 0
Your lookup table for this could be:
x 1 0 0 0 -> one pair
x 2 0 0 0 -> two pairs
x 0 1 0 0 -> three of a kind
x 1 1 0 0 -> full house
x 0 0 1 0 -> four of a kind
x 0 0 0 1 -> five of a kind
The 'x' means that you don't match on this. So, your lookup table matches four numbers to a score.
I was rather interested in this problem, so I have written some code to do the above. You'll still need to do the lookup table, but that is relatively trivial, and will be good practice for you. Here is a demo, with comments (run code here):
<?php
function counting(array $array) {
// Input figures
print_r($array);
// Run the figures twice through the bucket-counter
$firstBuckets = bucketCounter($array);
$secondBuckets = bucketCounter($firstBuckets);
// Ignore counts of 1
array_shift($secondBuckets);
// Output, just need to do the lookup now
echo ' converts to ';
print_r($secondBuckets);
echo "<br />";
}
/**
* Bucket counter
*/
function bucketCounter(array $array) {
$result = array(0, 0, 0, 0, 0, );
foreach($array as $value) {
if ($value > 0) {
$result[$value - 1]++;
}
}
return $result;
}
// Try some demos here!
counting(array(1, 2, 3, 4, 5));
counting(array(1, 1, 2, 4, 2));
counting(array(1, 1, 1, 1, 1));
?>
The demos I've included seem to work, but do hunt for bugs!
If the range is quite small, you can use counting sort approach. For each number, provide a "bucket" to count how many times a number appear. Scan once to fill in the buckets. Then another scan, but this time against the bucket to get the highest value. That's your score.
I've got an array per default:
array( 1=>1,2 =>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9 )
Now an operation is possible to change the numbers for example into:
array( 1=>1, 2=>1, 3=>3, 4=>1, 5=>1, 6=>6, 7=>7, 8=>8, 9=>9 )
--> changed the value of key 1, 2, 4 and 5
After this I need the following result
array( 1=>1, 2=1, 3=>2, 4=>1, 5=>1, 6=>3, 7=>4, 8=>5, 9=>6 )
--> changed the value of key 3, 6, 7, 8, 9 in the right order that no number is missing like the operation has done above.
Its a grid 3x3. Position 1 is 1, Position 2 is 2 and so on. Now a database could set that Position 1, 2, 4, 5 are the same and threated as 1. So the database sends: 1, 1, 3, 1, 1, 6, 7, 8, 9. Now the field from place 3 is 3, but should be the field from 2. Also the field from 6 must now be field 3 and so on.
Note: The operation is able to change every value in the array. for example the value of the
keys 4, 5, 7, 8
How can I do this?
Create a variable, in which you will store the max number. If you then iterate, check if the number from the array is lower then max, and if it is, then you do nothing, and if it is larger than max, so you put it in the third array and increase max.
$max = 0; // maximum value
$array3 = array(); // output array
foreach($array2 as $key=>$element){ // iterate for all elements
if($array2[$key] > $max){
$max++;
$array3[$key] = $max;
}
else
$array3[$key]=1; // *
} // end foreach
In the line marked with // * it puts 1 everytime. You may need to search if the value was once before, because it does not need to be always 1. You may use for example something like array_search.