Why does duplications happen in Heap's algorithm - php

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)));
}

Related

How to merge 3 arrays keeping their meta key?

I'm Getting some arrays from some wordpress custom fields:
$content = array(get_post_meta($postId, 'content'));
$media = array(get_post_meta($postId, 'media'));
$yt = array(get_post_meta($postId, 'youtube'));
I then need to have it printing in sequence, like:
media
content
LInk
Embed
And repeat the sequence for each value
media
content
LInk
Embed
For the sequence I'd use this:
echo '<ul>';
for ($i = 0; $i < count($all_array['media']); $i++) {
for ($j = 0; $j < count($all_array['content']); $j++) {
for ($k = 0; $k < count($all_array['youtube']); $k++) {
echo '<li>media->' . $all_array['media'][$i] . '</li>';
echo '<li>content->' . $all_array['content'][$j] . '</li>';
echo '<li>link->' . $all_array['link'][$k] . '</li>';
}
}
}
echo '</ul>';
But I'm doing something wrong with the merging of the 3 fields as if I do a var_dump before to run the for bit, like
echo '<pre>' . var_export($all_array, true) . '</pre>';
Then this is what I get and I cannot iterate as I wish:
array (
0 =>
array (
0 =>
array (
0 => '
brother
',
1 => '
Lorem
',
2 => '
End it
',
),
1 =>
array (
0 => '337',
1 => '339',
),
2 =>
array (
0 => 'https://www.youtube.com/watch?v=94q6fzbJUfg',
),
),
)
Literally the layout in html that I'm looking for is:
image
content
link
image
content
link
...
UPDATE
This how I am merging the arrays:
foreach ( $content as $idx => $val ) {
$all_array[] = [ $val, $media[$idx], $yt[$idx] ];
}
This is the associative array how it looks like:
Content:
array (
0 =>
array (
0 => '
brother
',
1 => '
Lorem
',
2 => '
End it
',
),
)
Media
array (
0 =>
array (
0 => '337',
1 => '339',
),
)
Youtube
array (
0 =>
array (
0 => 'https://www.youtube.com/watch?v=94q6fzbJUfg',
),
)
The way I've resolved it is first I calculate the tot. items within each array, then I get the array with max items and loop and add the items in sequence:
//GET CUSTOM FIELDS
$content = get_post_meta($post_to_edit->ID, 'content', false);
$media = get_post_meta($post_to_edit->ID, 'media', false);
$yt = get_post_meta($post_to_edit->ID, 'youtube', false);
$max = max(count($content), count($media), count($yt));
$combined = [];
//
// CREATE CUSTOM FIELDS UNIQUE ARRAY
for($i = 0; $i <= $max; $i++) {
if(isset($media[$i])) {
$combined[] = ["type" => "media", "value" => $media[$i]];
}
if(isset($content[$i])) {
$combined[] = ["type" => "content", "value" => $content[$i]];
}
if(isset($yt[$i])) {
$combined[] = ["type" => "youtube", "value" => $yt[$i]];
}
}
Finally I can loop:
foreach ($combined as $key => $val) {
if($val['type'] === "media") {
...
}
if($val['type'] === "content") {
...
You don't need to merge the arrays together. It will work fine in separate arrays. However, your for loops don't have the right logic. Try:
for ($i = 0; $i < count($media); $i++) {
for ($j = 0; $j < count($media[$i]); $j++) {
echo '<li>media->' . $media[$i][$j] . '</li>';
}
for ($j = 0; $j < count($content[$i]); $j++) {
echo '<li>content->' . $content[$i][$j] . '</li>';
}
for ($j = 0; $j < count($youtube[$i]); $j++) {
echo '<li>link->' . $youtube[$i][$j] . '</li>';
}
}

calcultion based on items minus stock value php

I have a calcultion based on items minus stock value (with some community help). If it hits zero then the next row will be touched.
$items = 45;
$stockRows = [40, 50, 60];
$newStock = function ($items, $stock) {
foreach ($stock as &$item) {
$delta = $item - $items;
$item = $delta > 0 ? $delta : 0;
$items = $delta < 0 ? abs($delta) : 0;
}
return $stock;
};
print_r($newStock($items, $stockRows));
Output
Array
(
[0] => 0
[1] => 45
[2] => 60
)
Now I want to add a row identifier so that I can update the database rows afterwards in a foreach and set the correct stock amount
$stockRows =
array(
array(
'id' => 1,
'amount' => 30
),
array(
'id' => 2,
'amount' => 40
),
array(
'id' => 3,
'amount' => 50
)
);
I cant get this to work. And if I get a output the first array result returns id 0.
Fixed it myself
$items = 45;
$ids = [1, 2, 3];
$stockRows = [40, 50, 60];
$newStock = function ($afschrijving, $stock, $ids) {
$i = 0;
foreach ($stock as &$item) {
$delta = $item - $afschrijving;
$item = array($id[$i++], $delta > 0 ? $delta : 0);
$afschrijving = $delta < 0 ? abs($delta) : 0;
}
return $stock;
};
print_r($newStock($items, $stockRows, $ids));

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;
}

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);
?>

PHP Trouble writing a reordering function for a 2-dimensional array of arbitrary length

Broadly speaking, I have a 2-dimensional array of the following format:
$elements = array( 0 => array('typeA', 'desc'),
1 => array('typeB', 'desc'),
2 => array('typeA', 'desc'),
n => array('typeC', 'desc'));
Where typeX can be 1 of 5 possibilities, and desc can be anything. The end goal is $elements sorted such that no two elements who share a typeX are ever adjacent. Here's my function:
function fixDbls($elems) {
$final = array();
$singles = array();
$doubles = array();
$lastelem = null;
foreach($elems as $elem) {
if(!$lastelem) { // set this the first time through
$lastelem = $elem[0];
$singles[] = $elem;
} else { //otherwise, sort!
if($lastelem == $elem[0]) {
$doubles[] = $elem;
} else {
$singles[] = $elem;
}
}
}
if ($doubles) {
// I suspect this is where it all goes wrong, I am awful at recursion!
$final = fixDbls(array_merge($singles, $doubles));
} else {
$final = $singles;
}
return $final;
}
If anyone can help me understand why this doesn't work (not just the code, but, where I've made a false assumption or where my thinking about this problem betrayed meā€”helps makes this more generally useful to the public!) I'd be ever, ever so appreciative.
I've been thinking your problem over and I think I came up with a solution. Here's the
code:
<?php
function print_array( $s, $a )
{
echo $s.': { ';
foreach ( $a as $k => $aa ) {
echo $k.' => ';
if ( is_array($aa) ) {
echo '{ '.implode( ', ', $aa ).' }, ';
} else {
echo $aa.', ';
}
}
echo '}'.PHP_EOL;
}
function search_array( array $a, $k )
{
$found = false;
foreach ( $a as $kk => $aa ) {
if ( $aa[0] == $k ) {
$found = $kk;
break;
}
}
return $found;
}
$input = array(
array('typeA', 'desc'),
array('typeB', 'desc'),
array('typeA', 'desc'),
array('typeC', 'desc')
);
print_array( 'Initial input', $input );
$frequencies = array();
foreach ( $input as $e ) {
$frequencies[ $e[0] ] = array_key_exists( $e[0], $frequencies ) ? $frequencies[ $e[0] ] + 1 : 1;
}
arsort($frequencies);
print_array( 'Frequencies', $frequencies );
$tail = array_slice( $frequencies, 1 );
$maxFreq = current( $frequencies );
$orderedElems = array_keys( $frequencies );
$mostFreq = current( $orderedElems );
echo 'The most frecuent element is "'.$mostFreq.'"'.PHP_EOL;
if ( array_sum( $tail ) < $maxFreq - 1 ) {
die ('There\'s No possible solution'.PHP_EOL);
}
$ouput = array();
for ( $i = 0; $i < $maxFreq; $i++ ) {
$k = search_array( $input, $mostFreq);
$output[] = $input[ $k ];
unset( $input[ $k ] );
}
print_array( 'Input after removing "'.$mostFreq.'"', $input );
echo '-----'.PHP_EOL;
print_array( 'Before process, output', $output );
foreach ( $tail as $e => $f ) {
$i = 1;
echo 'Elem to place: "'.$e.'" ('.$f.' times)'.PHP_EOL;
while ( ( $k = search_array( $input, $e ) ) !== false ) {
echo '$i: '.$i.PHP_EOL;
$begin = array_slice( $output, 0, $i );
print_array( 'Begin', $begin );
$end = array_slice( $output, $i );
print_array( 'End', $end );
$output = array_merge( $begin, array( $input[$k] ), $end );
print_array( 'Output', $output );
$i+=2;
unset( $input[$k] );
echo PHP_EOL;
}
}
print_array( 'Final output', $output );
This time I just tried the example you put in the question. The end result was:
Final output: { 0 => { typeA, desc }, 1 => { typeB, desc }, 2 => { typeC, desc }, 3 => { typeA, desc }, }
I hope this version suits your needs.
I made it a function now. This should work, the best.
$elements = array(0 => array('typeA', 'desc'), 1 => array('typeA', 'desc'), 2 => array('typeB', 'desc'), 3 => array('typeC', 'desc'), 4 => array('typeC', 'desc'), 5 => array('typeB', 'desc'), 6 => array('typeB', 'desc'), 7 => array('typeD', 'desc'), 8 => array('typeD', 'desc'), 9 => array('typeA', 'desc'), 10 => array('typeA', 'desc'));
function sortDeep($ary){
foreach($ary as $a){
foreach($a as $i => $v){
if($i === 0)$typesArray[] = $v;
}
}
function notNextTo($a, $b){
if($a === $b){
return 1;
}
else{
return 0;
}
}
uasort($typesArray, 'notNextTo'); $ak = array_keys($typesArray);
foreach($ary as $i => $v){
$sorted[$i] = $ary[$ak[$i]];
}
return $sorted;
}
print_r(sortDeep($elements));

Categories