Permutations for all possible sets ( 3 digits) including zero - php

I am creating a small game program in which there are two types of games. Single and Double.
For each game there is a fixed chart set.
Single Chart
'128', '129', '120', '130', '140', '123', '124', '125', '126', '127',
'137', '138', '139', '149', '159', '150', '160', '134', '135', '136',
'146', '147', '148', '158', '168', '169', '179', '170', '180', '145',
'236', '156', '157', '167', '230', '178', '250', '189', '234', '190',
'245', '237', '238', '239', '249', '240', '269', '260', '270', '235',
'290', '246', '247', '248', '258', '259', '278', '279', '289', '280',
'380', '345', '256', '257', '267', '268', '340', '350', '360', '370',
'470', '390', '346', '347', '348', '358', '359', '369', '379', '389',
'489', '480', '490', '356', '357', '349', '368', '378', '450', '460',
'579', '570', '580', '590', '456', '367', '458', '459', '469', '479',
'560', '589', '670', '680', '690', '457', '467', '468', '478', '569',
'678', '679', '689', '789', '780', '790', '890', '567', '568', '578',
Double Chart
'100', '110', '166', '112', '113', '114', '115', '116', '117', '118',
'119', '200', '229', '220', '122', '277', '133', '224', '144', '226',
'155', '228', '300', '266', '177', '330', '188', '233', '199', '244',
'227', '255', '337', '338', '339', '448', '223', '288', '225', '299',
'335', '336', '355', '400', '366', '466', '377', '440', '388', '334',
'344', '499', '445', '446', '447', '556', '449', '477', '559', '488',
'399', '660', '599', '455', '500', '600', '557', '558', '577', '550',
'588', '688', '779', '699', '799', '880', '566', '800', '667', '668',
'669', '778', '788', '770', '889', '899', '700', '990', '900', '677',
When I select a Single and entered number digit 0123456789 (any sequence(sorted & unsorted), min 4 digits & max 10 digits from 0-9, repeated), it returns the entire set which is related to Single Chart.
For this case it returns 120 sets.
If I select double and entered same digit 0123456789 it returns all the set which is related to Double Chart ONLY.
Currently I am using one solutions for Single game which is given here permutations-all-possible-sets-of-numbers but in some set it is returned like 001 but it has to be 100
Also I am not able to find the solutions for Double game.
What I try :
public function insertSingleGameData($motorGameData)
{
$chartvalidation = config('chartValidation');
$tempMotorSet = str_split($motorGameData->Playgame->cane);
$motorSet = $this->arrayCombination(3, $tempMotorSet); // Get All Sets
$motorRange = array_unique($motorSet);
foreach($motorRange as $cane)
{
if(in_array($cane, $chartvalidation['single'])){
$tempGameData[] = ([
'playgame_id' => $motorGameData->Playgame->id,
'user_id' => $motorGameData->Playgame->user_id,
'cane' => $cane,
'amount' => $motorGameData->Playgame->amount,
'gamemaster_id' => $motorGameData->Playgame->gamemaster_id,
"created_at" => \Carbon\Carbon::now(),
"updated_at" => \Carbon\Carbon::now(),
]);
}
}
Tempgame::insert($tempGameData);
}
function arrayCombination($le, $set){
$lk = $this->combination_number($le, count($set));
$ret = array_fill(0, $lk, array_fill(0, $le, '') );
$temp = array();
for ($i = 0 ; $i < $le ; $i++)
$temp[$i] = $i;
$ret[0] = $temp;
for ($i = 1 ; $i < $lk ; $i++){
if ($temp[$le-1] != count($set)-1){
$temp[$le-1]++;
} else {
$od = -1;
for ($j = $le-2 ; $j >= 0 ; $j--)
if ($temp[$j]+1 != $temp[$j+1]){
$od = $j;
break;
}
if ($od == -1){
break;
}
$temp[$od]++;
for ($j = $od+1 ; $j < $le ; $j++) {
$temp[$j] = $temp[$od]+$j-$od;
}
}
$ret[$i] = $temp;
}
for ($i = 0 ; $i < $lk ; $i++) {
for ($j = 0 ; $j < $le ; $j++){
$ret[$i][$j] = $set[$ret[$i][$j]];
}
}
$tempSet = array();
foreach ($ret as $key => $value) {
$tempSet[] = implode('', $value);
}
return $tempSet;
//print("<pre>".print_r($ret,true)."</pre>");
}
function combination_number($k,$n){
$n = intval($n);
$k = intval($k);
if ($k > $n){
return 0;
} elseif ($n == $k) {
return 1;
} else {
if ($k >= $n - $k){
$l = $k+1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $n-$k ; $i++)
$m *= $i;
} else {
$l = ($n-$k) + 1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $k ; $i++)
$m *= $i;
}
}
return $l/$m;
}
How can I achieve this both Single and Double game in a one function?

You can do a preg_match on numbers by creating regular expression such as:
/^[235046]+$/
This means we are trying to match a string which has digits 235046(meaning, 2 or 3 or 5 and so on) right from start (^ caret symbol) till end ($ dollar symbol). If we find a match, we collect them in another array.
Snippet:
<?php
$digits = '235046';
$single_chart_filtered = [];
foreach($single_chart as $chart_value){
if(preg_match("/^[$digits]+$/",$chart_value) === 1){
$single_chart_filtered[] = $chart_value;
}
}
print_r($single_chart_filtered);
$double_chart_filtered = [];
foreach($double_chart as $chart_value){
if(preg_match("/^[$digits]+$/",$chart_value) === 1){
$double_chart_filtered[] = $chart_value;
}
}
Demo: https://3v4l.org/jChvm

001 IS a valid combination of 0123456789. You need to filter your array for the possible set:
// simple artificial setup
$single = [111,222,333,444,555];
function generatePossibleSet($input) { return [000,001,010,100,011,101,110,111]; }
$possibleSet = generatePossibleSet("01");
$set = $single;
// just interscet the picked set with the possible set
$set = array_intersect($set, $possibleSet);
This example will give [111] - the only valid combination of 0 and 1 that was in the set-list.

Related

Php weak random value generator for cases where repeated values are useful

I am generating test data for use in my frontend and I want to generate ages. Currently I am generating age between 18 and 100 using rand(1,100) like this:
require_once 'vendor/autoload.php';
$faker = Faker\Factory::create();
$superstars = array("Adam Cole","Finn Balor","Pete Dunne","Jordan Devlin","Noam Dar");
$fan_favourite_superstar_name = $superstars[ mt_rand( 0, count($superstars) -1 ) ];
$cities = array("London","Manchester","Leeds","Bristol");
$fan_location = $cities[ mt_rand( 0, count($cities) -1 ) ];
$the_models = array("Iphone","Nokia","Huawei","Samsung");
$fan_phone_model = $the_models[ mt_rand( 0, count($the_models) -1 ) ];
$array = Array (
"0" => Array (
"id" => uniqid(),
"fan_favourite_superstar_name" => $fan_favourite_superstar_name,
"fan_location" => $fan_location,
"fan_phone_model" => $fan_phone_model,
"fan_name" => $faker->name,
"fan_age" => rand(18,100),
"fan_comments" => $faker->text,
"fan_picture" => rand(1,500),
"last_updated" => time() + rand(1,1000),
),
However, I would like my ages to repeat although not completely. Is there a weak randomness generator apart from rand that can ensure that the an ages generated randomly repeat themselves n times?.
Here is a function just to show my suggestion of solution:
function getAge($start = 18, $end = 100, $repeat = 20){
$result = null;
static $ages = array();
if ( empty($ages) ) {
for ($i= $start; $i <= $end; $i++) {
for($j = 0; $j < $repeat; $j++)
$ages[] = $i;
}
}
$index = rand(0, count($ages));
$result = $ages[ $index ];
unset($ages[ $index ]);
$ages = array_values($ages);
return $result;
}

Why does duplications happen in Heap's algorithm

I want to get all permutations from elements of array. Source array is very simple:
$arr = [ 1,2,3,4 ];
I wrote the code for implement Heap's algorithm,
private function mixture( $size, array $collection ) {
$permutations = [];
$offset = $size - 1;
if ( 1 === $size ) {
$permutations[] = implode( '-', $collection );
return $permutations;
}
for ( $i = 0; $i < $offset; $i++ ) {
$permutations = array_merge( $permutations, $this->mixture( $offset, $collection ) );
$j = ( 0 == $size % 2 ) ? $i : 0;
$tmp_el = $collection[ $offset ];
$collection[ $offset ] = $collection[ $j ];
$collection[ $j ] = $tmp_el;
}
$permutations = array_merge( $permutations, $this->mixture( $offset, $collection ) );
return $permutations;
}
The result of works has a many of duplications
array (size=24)
0 => '1-2-3-4' << same 4
1 => '2-1-3-4' << same 5
2 => '3-2-1-4'
3 => '2-3-1-4'
4 => '1-2-3-4' << same 0
5 => '2-1-3-4' < same 1
6 => '4-2-3-1'
7 => '2-4-3-1'
8 => '3-2-4-1'
9 => '2-3-4-1'
10 => '4-2-3-1'
11 => '2-4-3-1'
12 => '4-1-3-2'
13 => '1-4-3-2'
14 => '3-1-4-2'
15 => '1-3-4-2'
16 => '4-1-3-2'
17 => '1-4-3-2'
18 => '4-1-2-3'
19 => '1-4-2-3'
20 => '2-1-4-3'
21 => '1-2-4-3'
22 => '4-1-2-3'
23 => '1-4-2-3'
Please, help me to understand a reason for this and fix the code. I want to remove any duplication from the result.
Thanks
Your only problem is that you need to pass the $collection by reference because PHP creates an array copy by default:
mixture( $size, array &$collection )
https://3v4l.org/7Vn2p
<?php
$arr = [ 1,2,3,4 ];
$expected = [
'1-2-3-4',
'2-1-3-4',
'3-1-2-4',
'1-3-2-4',
'2-3-1-4',
'3-2-1-4',
'4-2-1-3',
'2-4-1-3',
'1-4-2-3',
'4-1-2-3',
'2-1-4-3',
'1-2-4-3',
'1-3-4-2',
'3-1-4-2',
'4-1-3-2',
'1-4-3-2',
'3-4-1-2',
'4-3-1-2',
'4-3-2-1',
'3-4-2-1',
'2-4-3-1',
'4-2-3-1',
'3-2-4-1',
'2-3-4-1',
];
function mixture( $size, array &$collection ) {
$permutations = [];
$offset = $size - 1;
if ( 1 === $size ) {
$permutations[] = implode( '-', $collection );
return $permutations;
}
for ( $i = 0; $i < $offset; $i++ ) {
$permutations = array_merge( $permutations, mixture( $offset, $collection ) );
$j = ( 0 == $size % 2 ) ? $i : 0;
$tmp_el = $collection[ $offset ];
$collection[ $offset ] = $collection[ $j ];
$collection[ $j ] = $tmp_el;
}
$permutations = array_merge( $permutations, mixture( $offset, $collection ) );
return $permutations;
}
print_r($permutations = mixture( count($arr), $arr ));
if ($expected == $permutations) {
echo 'PASS'.PHP_EOL;
} else {
echo 'FAIL'.PHP_EOL;
echo 'missing: '.PHP_EOL;
print_r(array_diff($expected, array_unique($permutations)));
}

PHP & Array: count distance between two keys

Having an array similar to the one below:
$steps = array(0 => 'aaa', 1 => 'bbb', 2 => 'ccc', ......, 7 => 'hhh', 8 => 'iii', .....);
How can I calculate how many steps (key) do I need to reach key 2 from key 7 respecting the sequence?
If you have numeric keys that never have any missing numbers, you can use basic subtraction.
If you need to account for possible missing numbers, or the keys are not numeric, you can use a combination of array_keys() and array_search():
$array = array(
0 => 'aaa',
1 => 'bbb',
3 => 'ccc',
'four' => 'ddd',
900 => 'eee',
13 => 'fff'
);
$from = 1;
$to = 900;
$keys = array_keys($array);
$from_index = array_search($from, $keys); // 1
$to_index = array_search($to, $keys); // 4
$steps = $to_index - $from_index;
// 3 steps: from 1-3, from 3-'four' and from 'four'-900
I solved this problem by writing this code:
$tot_step = 0;
$steps = array(
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
3 => 'ddd',
4 => 'eee',
5 => 'fff',
6 => 'ggg',
7 => 'hhh',
8 => 'iii',
9 => 'jjj',
10 => 'aaa'
);
$from = "ddd";
$to = "bbb";
$from_index = array_search($from, $steps);
$to_index = array_search($to, $steps);
$last = $steps[(count($steps)-1)];
if ($from_index > 0) {
if ($to == $last)
$to_index = (count($steps)-1);
$arr_l = count($steps);
$mila = array();
for ($ind = $from_index; $ind <= ($arr_l-1); $ind++) {
if ($to == $last) {
if ($steps[$ind] != $last)
$mila[] = $steps[$ind];
} else {
$mila[] = $steps[$ind];
}
unset($steps[$ind]);
}
if (!empty($mila)) {
for ($i = (count($mila)-1); $i >= 0; $i--)
array_unshift($steps, $mila[$i]);
}
$to_new = array_search($to, $steps);
foreach ($steps as $key => $value) {
if ($key == $to_new)
break;
else
$tot_step++;
}
} elseif ($from_index == 0) {
if ($to_index == $from_index) {
$tot_step = (count($steps)-1);
} else {
foreach ($steps as $key => $value) {
if ($key == $to_index)
break;
else
$tot_step++;
}
}
}
echo $tot_step;
I hope it will be useful to someone

Check date ranges (start and end date) for overlap

function checkDateOverlap($ranges) {
$res = $ranges[0];
$countRanges = count($ranges);
for ($i = 0; $i < $countRanges; $i++) {
$r1s = $res['start'];
$r1e = $res['end'];
$r2s = $ranges[$i]['start'];
$r2e = $ranges[$i]['end'];
if ($r1s >= $r2s && $r1s <= $r2e || $r1e >= $r2s && $r1e <= $r2e || $r2s >= $r1s && $r2s <= $r1e || $r2e >= $r1s && $r2e <= $r1e) {
$res = array(
'start' => $r1s > $r2s ? $r1s : $r2s,
'end' => $r1e < $r2e ? $r1e : $r2e
);
} else
return false;
}
return $res;
}
// example of returned dates that overlap
$ranges = array(
array('start' => '2014-01-01', 'end' => '2014-01-04'),
array('start' => '2014-01-05', 'end' => '2014-01-10'),
array('start' => '2014-01-04', 'end' => '2014-01-07')
);
//example of failure
$ranges2 = array(
array('start' => '2014-01-01', 'end' => '2014-01-04'),
array('start' => '2014-01-05', 'end' => '2014-01-10'),
array('start' => '2014-01-11', 'end' => '2014-01-17')
);
var_dump(checkDateOverlap($ranges));
The following is what I was attempting to check intersection of date ranges. In the array "ranges1" this example has overlapping dates. It should return the dates. In array $ranges2, this should pass as no intersecting dates.
Now the weird thing is the start and end date can be the exact same, so you could make an entry for just a single day. I've tried many things, and I'm stumped.
I believe there needs to be another for loop, but regardless I am not getting success.
Here was another go I had:
<?php
// pass your ranges to this method and if there is a common intersecion it will
// return it or false
function checkDateOverlap($ranges){
$res = $ranges[0];
$countRanges = count($ranges);
for ($i = 0; $i < count($countRanges); $i++) {
for($j = $i+1; $j < count($countRanges); $j++) {
$r1s = $res['start'];
$r1e = $res['end'];
$r2s = $ranges[$i]['start'];
$r2e = $ranges[$i]['end'];
if (($r1s >= $r2e && $r2s <= $r1e)) {
$res[] = array(
'start' => $r1s > $r2s ? $r1s : $r2s,
'end' => $r1e < $r2e ? $r1e : $r2e
);
} else
return false;
}
}
return $res;
}
// example
$ranges = array(
array('start' => '2014-01-04', 'end' => '2014-01-05'),
array('start' => '2014-01-06', 'end' => '2014-01-10'),
array('start' => '2014-01-11', 'end' => '2014-01-13')
);
echo "<pre>";
var_dump(checkDateOverlap($ranges));
echo "</pre>";
Any advice greatly appreciated.
$ranges = array(
array('start' => new DateTime('2014-01-01'), 'end' => new DateTime('2014-01-05')),
array('start' => new DateTime('2014-01-06'), 'end' => new DateTime('2014-01-06')),
array('start' => new DateTime('2014-01-07'), 'end' => new DateTime('2014-01-07')),
);
function intersects($lhs, $rhs) {
// Note that this function allows ranges that "touch",
// eg. one pair starts at the exact same time that the other ends.
// Adding less "or equal to" will allow same start date
return !($lhs['start'] > $rhs['end'] || $lhs['end'] < $rhs['start']);
}
function checkDates($ranges) {
// Comparison loop is of size n•log(n), not doing any redundant comparisons
for($i = 0; $i < sizeof($ranges); $i++) {
for($j = $i+1; $j < sizeof($ranges); $j++) {
if(intersects($ranges[$i], $ranges[$j])) {
echo "Date {$i} intersects with date {$j}\n";
}
}
}
}
checkDates($ranges);
I've attached my working code sample to hopefully help someone else in the future looking for the same solution. This will print the arrays that intersect.
If you use usort to first sort the dates, the work gets a lot easier. The following can be optimized a lot, but it is done step-by-step make it easier to understand.
//The date comparison function, sort on start and then on end
function cmp($a, $b)
{
if($a['start']<$b['start']) return -1;
if($a['start']>$b['start']) return 1;
if($a['end']<$b['end']) return -1;
if($a['end']>$b['end']) return 1;
return 0; // start=start and end=end
}
$ranges = array(
array('start' => '2014-01-01', 'end' => '2014-01-04'),
array('start' => '2014-01-05', 'end' => '2014-01-10'),
array('start' => '2014-01-04', 'end' => '2014-01-07')
);
usort($ranges, 'cmp'); // Sort the dates
$output = array();
for($i=0; $i<sizeof($ranges); $i++)
{
$endindex = $i; // The index containing the proper 'end' value
for($j=$i+1; $j<sizeof($ranges); $j++)
{
if($ranges[$endindex]['start'] == $ranges[$j]['start']) // Overlap
$endindex = $j;
elseif($ranges[$endindex]['end']>=$ranges[$j]['start']) // Overlap
$endindex = $j;
}
$output[] = array('start' => $ranges[$i]['start'], 'end' => $ranges[$endindex]['end']);
// Break the rules by hard-setting $i from the for loop - it works great in this case
$i = $endindex;
}
print_r($output);
It works for your example. If you have other rules that must be used, hopefully you can adjust this code.
Here are some remarks:
- You do not check the validity of the date formed by 'start' and 'end'.
- Why do you not convert the dates to timestamp ?
-> It's more easier and faster to compare integer value instead of string ?
Why do you not use PHP DateTime and DateInterval Objects ?
http://php.net/manual/en/book.datetime.php

JSON encoding from wordpress database values

I need a proper JSON file with "geo_langitude", "geo_latitude", "adress" & "url" values
I have wp database with post_meta "property" - "geo_langitude" "geo_latitude" & "address" in there.
my file is smth like that
<?php
function return_markers($count) {
$query = new WP_Query('post_type=property&posts_per_page=-1&orderby=menu_order&order=DESC&post_status=publish');
$array = array();
$i = 0;
for ($i = 0; $i< $count; $i++) {
$elem = array(
't' => 'string '.$i,
'x' => get_post_meta($post->ID,'geo_latitude',true),
'y' => get_post_meta($post->ID,'geo_longitude',true),
'z' => $i
);
$array[] = $elem;
}
echo json_encode($elem);
};
return_markers($i);
?>
It's my first time using JSON so I have troubles and I don't know where. =(
with random markers work perfectly:
<?php
function return_markers($count) {
$array = array ();
for ($i = 0; $i< $count; $i++) {
$elem = array(
't' => 'string '.$i,
'x' => 23 + (rand ( 0 , 10000 ) / 10000) * 5,
'y' => 56.2 + (rand ( 0 , 10000 ) / 10000) * 0.8,
'url' => '#map_redirect_'.$i,
'z' => $i
);
$array[] = $elem;
}
echo json_encode($array);
};
return_markers(23);
?>

Categories