PHP array of elements, sort by circular "animation"? - php

I've already asked a similar question, but I need a different effect. The original question is here.
I have a simple array. The array length is always a square number. So 16, 25, 36 etc.
$array = array('1', '2', '3', '4' ... '25');
What I do, is arrange the array with HTML so that it looks like a block with even sides.
What I want to do, is sort the elements, so that when I pass the JSON encoded array to jQuery, it will iterate the array, fade in the current block, and so I'd get a circular animation. So I'd like to sort the array kind of like this
So my sorted array would look like
$sorted = array('1', '6', '11'', '16', '21', '22', '23' .. '13');
Is there way to do so?.. Thanks
Edit:
I'm trying to do this by creating matrix-like column/row arrays with this:
$side = 5;
$elems = $side*$side;
$array = range(1,$elems);
for($i=1; $i <= $side; $i++) {
for($x=$i; $x <= $elems; $x=$x+$side) {
$columns[$i][] = $x;
}
}
for($i=1, $y=1; $i <= $elems; $i=$i+$side, $y++) {
for($x=$i; $x < $side+$i; $x++) {
$rows[$y][] = $x;
}
}
My next step is to go down the first column, at the end if it go right on the last elements column, at the end up on the last element etc.. If anyone has a better idea that would be great :)

This will work as long as the grid is always square:
<?php
// The size of the grid - 5x5 in the example above
$gridSize = 5;
// Create a 2D array representing the grid
$elements = array_chunk(range(1, pow($gridSize, 2)), $gridSize);
// Find the half way point - this will be the end of the loop since we
// want to stop in the middle
$end = ceil($gridSize / 2);
// An array to hold the result
$result = array();
// The stopping point of the current interation
$stop = $gridSize;
// Loop from start to the middle
for ($i = 0; $i < $end; $i++) {
// start in the top left corner
$x = $y = $i;
// Traverse Y top to bottom
while ($y < $stop) {
$result[] = $elements[$y++][$x];
}
$y--;
$x++;
// Traverse X left to right
while ($x < $stop) {
$result[] = $elements[$y][$x++];
}
$x--;
$y--;
// Traverse Y bottom to top
while ($y >= $gridSize - $stop) {
$result[] = $elements[$y--][$x];
}
$y++;
$x--;
// Make sure we come in a level
$stop--;
// Traverse X right to left
while ($x >= $gridSize - $stop) {
$result[] = $elements[$y][$x--];
}
}
print_r($result);
See it working

This should work. You can pass any array to circularSort function and it will return your sorted array.
/*
Get the circular sorted array
*/
function circularSort($array)
{
//Get the length of array
$arrayLength = count($array);
//Find the square root of length of array
$arrayRows = sqrt($arrayLength);
//Divide the arrays in $arrayRows
$arrayChunks = array_chunk($array,$arrayRows);
$circularArray = array();
//Call Circular Array function .. Result will be stored in $circularArray
circularArray($arrayChunks,$circularArray);
return $circularArray;
}
/*
Loop arrayChunk in following order
1. Fetch first item from each chunks
2. Fetch all items from last chunk and remove that array from arrayChunk
3. Reverse elements in each remaining chunk
4. Reverse entire arrayChunk array
5. Repeat above 4 steps until $arrayChunks is empty
*/
function circularArray(&$arrayChunks, &$circularArray)
{
if(empty($arrayChunks))
{
return true;
}
//1. Fetch first item from each chunks
foreach($arrayChunks as &$arrayChunk)
{
$circularArray[] = array_shift($arrayChunk);
}
//Fetch Last Chunk from array
$lastChunk = array_pop($arrayChunks);
//2. Fetch all items from last chunk and remove that array from arrayChunk
foreach($lastChunk as $chunkElement)
{
$circularArray[] = $chunkElement;
}
//3. Reverse elements in each remaining chunk
foreach($arrayChunks as &$arrayChunk)
{
if (is_array($arrayChunk))
{
$arrayChunk = array_reverse($arrayChunk);
}
}
$arrayChunks = array_reverse($arrayChunks);
return circularArray(&$arrayChunks, &$circularArray);
}
e.g.
$array = range(1, 25);
$circularArray = circularSort($array);

Another approach in O(n):
<?php
function circ_sort ($inArray) {
$rowSize = pow(count($inArray), 0.5);
if((int)$rowSize != $rowSize) {
throw new InvalidArgumentException();
}
$rowSize = (int)$rowSize;
$round =-1;
for ($x =-1, $y=0, $count =0; $count < count($inArray);) {
if ($y > $x) {
if ($x +1 == $y) {
$direction = 'D'; //Down
$round ++;
$max_iter = $rowSize - (2 * $round);
} else {
$direction = 'L'; //Left
$max_iter = $y - $x -1;
}
} else if ($x > $y) {
$direction = 'R'; //Right
$max_iter = $rowSize - (2 * $round) -1;
} else if ($x == $y) {
$direction = 'U'; //Up
$max_iter = $rowSize - (2 * $round) -1;
}
switch ($direction) {
case 'D': //Down
for ($iter =0; $iter < $max_iter; $iter++) {
$x++;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
case 'R': //Right
for ($iter =0; $iter < $max_iter; $iter++) {
$y++;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
case 'U': //Up
for ($iter =0; $iter < $max_iter; $iter++) {
$x--;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
case 'L': //Left
for ($iter =0; $iter < $max_iter; $iter++) {
$y--;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
}
}
return ($circArray);
}
$array = range(1, 25);
$circ_array = circ_sort($array);
var_dump($circ_array);
?>

My solution:
The trick is: The first run is 5 then two runs of 4 elements, two of 3 elements, 2 of 2 elements and two of 1 element. (5,4,4,3,3,2,2,1,1) In each run it is incremented a state module 4. Depending on the state the runs goes in one direction or other.
Here is the code:
function circularSort(array $array) {
$n2=count($array);
$n=sqrt($n2);
if((int)$n != $n) throw new InvalidArgumentException();
$Result = Array();
$run =$n; $dir=1;
$x=0; $y=-1;
$i=0;
$st=0;
while ($run) {
while ($dir) {
for ($j=0; $j<$run; $j++) {
if ($st==0) $y++;
if ($st==1) $x++;
if ($st==2) $y--;
if ($st==3) $x--;
$p=$y * $n +$x;
array_push($Result,$array[$p]);
}
$st = ($st +1) & 3;
$dir--;
}
$dir=2;
$run--;
}
return $Result;
}
$a = range(1,25);
var_dump(circularSort($a));

Related

I'm new to PHP and I created a For loop and would like to know if anyone knows how to re-write it to a while loop?

This is the code I have. It currently works as is, However I'm experimenting with loops and want to see it can be done with a while loop and how it would be done. With this code I can take 2 input numbers and display them, then point out all odds, add all evens, and add all the squares of the odds.
define ("B","<br/>");
$firstNum = $_POST["firstNum"];
$secondNum = $_POST["secondNum"];
if ($firstNum < $secondNum)
{
$firstNum = true;
}
elseif ($firstNum >= $secondNum)
{
$firstNum = "You didn't listen, dumb dumb!".'<br/>GO BACK';
}
echo "First Number: ".$firstNum."<br/>"."Second Number: ".$secondNum;
echo B;
echo B;
$numbers = array();
$numbers = range($firstNum, $secondNum);
$length = count($numbers);
$odds = array();
$sumSqOdds = 0;
$sumEven = 0;
$j = 0;
for ($x = 0; $x < $length; $x++)
{
if (($numbers[$x] % 2) == 1)
{
$odds[$j] = $numbers[$x];
$sumSqOdds = $sumSqOdds + pow ($numbers[$x], 2);
$j++;
}
else
{
$sumEven = $sumEven + $numbers[$x];
}
}
$x = 0;
$y = 0;
printf("The odd numbers between your integers are: ");
for ($x = 0; $x < $j; $x++)
{
echo $odds[$x];
echo ' ';
$y++;
if (($y % 10) == 0)
{
echo B;
}
}
echo B;
echo B;
printf("The sum of all even numbers between your integers is: ".$sumEven);
echo B;
echo B;
printf("The sum of the square of the odd numbers between your integers is: ".$sumSqOdds);
Here is my while loop but it seems to be infinite...
$numW = array ();
$numW = range ($firstNum, $secondNum);
$lengthW = count ($numW);
$oddsW = array ();
$sumSqOddsW = 0;
$sumEvenW = 0;
$j = 0;
$x = 0;
while ($x < $lengthW)
{
if (($numW[$x] % 2) == 1)
{
$oddsW[$j] = $numW[$x];
$sumSqOddsW = $sumSqOddsW + pow ($numW[$x], 2);
$x++;
$j++;
}
else
{
$sumEvenW = $sumEvenW + $numW[$x];
}
}
$x = 0;
$y = 0;
printf ("The odd numbers between your integers are: ");
while ($x < $j)
{
$x++;
echo $oddsW[$x];
echo "nbsp;";
$y++;
if (($y % 10) == 0)
{
echo B;
}
}
Equivalent loops:
for ($i = 0; $i < 10; $i++) {
echo $i;
}
$i = 0;
while ($i < 10) {
echo $i;
$i++;
}
For a loop to ever finish it has to change one of the two evaluating variables. So either $x, or $lengthW would have to change during iteration. You made an if statment, in the first case you define that X increases by 1, but in the else case you do not change any variable that then has an effect on either $x, or $lengthW
Nor is there any check that sees if the else state has been reached and to catch that by either changing $x or $lengthW in a later iteration.
As such there's an infinite loop as soon as you reach the else case.
The if statement uses the same $x value as the last iteration checking the same position of the $numW, as such nothing has changed since the last iteration and you'll hit the else again, and again, and so on.
while ($x < $lengthW)
{
if (($numW[$x] % 2) == 1)
{
$oddsW[$j] = $numW[$x];
$sumSqOddsW = $sumSqOddsW + pow ($numW[$x], 2);
$x++; //$x is increased by one, and as such, the loop will progress.
// remove this $x++ if you place it outside the if else statement.
$j++;
}
else // reached when ($numW[$x] %2) != 1
{
$sumEvenW = $sumEvenW + $numW[$x];
// No changes to $x or $lengthW as such you'll hit the else again
// this could be solved by either adding $x++; here.
}
// or by adding $x++; here
// (if you do add it here, remove it in the if case above,
// or you risk increasing it by 2 every iteration
}

Looking for opposite of pow() in PHP

I am working on mathematical problem where the formula is: A[i] * (-2) power of i
where i=0,1,2,3,...
A is an array having values 0 or 1
Input array: [0,1,1,0,0,1,0,1,1,1,0,1,0,1,1]
Output is: 5730
Code
$totalA = 0;
foreach ($A as $i => $a) {
$totalA += $a * pow(-2, $i);
}
This is correct. Now I am looking for its opposite like:
Input is: 5730
Output will be: [0,1,1,0,0,1,0,1,1,1,0,1,0,1,1]
I am not asking for the exact code but looking for some logic from where I should start. I tried to use log() method but that did not return the desired output.
You were not looking for exact code, but I found this problem too interesting. This works:
function sign($n) {
return ($n > 0) - ($n < 0);
}
$target = -2396;
$i = 0;
$currentSum = 0;
// Look for max $i
while (true) {
$val = pow(-2, $i);
$candidate = $currentSum + $val;
if (abs($target) <= abs($candidate)) {
// Found max $i
break;
}
if (abs($target - $candidate) < abs($target - $currentSum)) {
// We are getting closer
$currentSum = $candidate;
}
$i++;
}
$result = [];
for ($j = $i; 0 <= $j; $j--) {
$val = pow(-2, $j);
$border = $val / 4;
if (sign($val) == sign($target) && abs($border) < abs($target)) {
array_unshift($result, 1);
$target -= $val;
} else {
array_unshift($result, 0);
}
}
echo json_encode($result);
First I look for the $i that gets me on or slightly above the $target. When found, I walk down and decide for each bit if it should be in the result.

N random numbers that can duplicate after 2 elements

I want to generate 10 numbers with each ranging from (1 to 5) but can only duplicate after 2 elements
for example 5 3 1 4 2 5(can be duplicated here) 2 (cannot be duplicate here since it occur before 1 element) ...etc.
I have this code in php working but its performance is awful since it sometimes exceeds the maximum 30 seconds execution time.
<?php
function contain($prevItems, $number) {
if (count($prevItems) == 3)
{
array_shift($prevItems);
}
for ($k=0; $k<count($prevItems); $k++) {
if ($prevItems[$k] == $number)
return true;
}
return false;
}
$num[0] = rand(1,5);
$prevItems[0] = $num[0];
for ($i=1; $i<=10; $i++) {
$num[$i] = rand(1,5);
while (contain($prevItems, $num[$i])) {
$num[$i] = rand (1,5);
}
$prevItems[$i] = $num[$i]; //append into the array
}
print_r($num);
?>
Edit:
I have also tried this method, its performance is good but it duplicates elements
<?php
$evalAccurance = array();
$count = 0;
while ( $count < 11)
{
$random = rand(1, 5);
if (in_array($random, $evalAccurance))
{
$p = $random;
for ($k = $p ; $k <5; $k++)
{
$random = $random++;
if (in_array($random, $evalAccurance))
continue 1;
else break 1;
}
if (in_array($random, $evalAccurance))
{
for ($k = $p ; $k >0; $k--)
{
$random = $random--;
if (in_array($random, $evalAccurance))
continue 1;
else break 1;
}
}
}
$evalAccurance[] = $random;
if (count ($evalAccurance) == 4)
array_shift($evalAccurance);
print_r ($evalAccurance);
$count++;
}
?>
One way you could do this:
// pass to function current array of numbers
function randomNumber($ar){
// create a random number from 1 to 5
$num = rand(1,5);
// check backwards 3 elements for same number, if none found return it
if(!in_array($num, array_slice($ar, -3, 3, true))){
return $num;
} else {
// else recall function with same array of numbers
return randomNumber($ar);
}
}
$array = array();
// loop 10 numbers and call randomNumber with current set of results.
for($i=1; $i<=10; $i++){
$array[] = randomNumber($array);
}
print_r($array);
Using PHP SPLQueue:
$queue = new SplQueue();
$values = array(1, 2, 3, 4, 5);
$container = array();
for ($i = 0; $i < 10; $i++) {
$value = give_random($values, $queue);
$container[] = $value;
if ($queue->offsetExists(1) AND $queue->offsetExists(0)) {
$queue->dequeue();
}
$queue->enqueue($value);
}
function give_random(&$values, &$queue) {
$picked_value = $values[array_rand($values)];
if ($queue->offsetExists(1)) {
if ($picked_value == $queue->offsetGet(1)) {
$picked_value = give_random($values, $queue);
}
}
if ($queue->offsetExists(0)) {
if ($picked_value == $queue->offsetGet(0)) {
$picked_value = give_random($values, $queue);
}
}
return $picked_value;
}
print_r($container);
It could be neater, but you can figure what's going on.
Cheers.

What are better ways to insert element in sorted array in PHP

I've recently send my CV to one company that was hiring PHP developers. They send me back a task to solve, to mesure if I'm experienced enough.
The task goes like that:
You have an array with 10k unique elements, sorted descendant. Write function that generates this array and next write three different functions which inserts new element into array, in the way that after insert array still will be sorted descendant. Write some code to measure speed of those functions. You can't use PHP sorting functions.
So I've wrote function to generate array and four functions to insert new element to array.
/********** Generating array (because use of range() was to simple :)): *************/
function generateSortedArray($start = 300000, $elementsNum = 10000, $dev = 30){
$arr = array();
for($i = 1; $i <= $elementsNum; $i++){
$rand = mt_rand(1, $dev);
$start -= $rand;
$arr[] = $start;
}
return $arr;
}
/********************** Four insert functions: **************************/
// for loop, and array copying
function insert1(&$arr, $elem){
if(empty($arr)){
$arr[] = $elem;
return true;
}
$c = count($arr);
$lastIndex = $c - 1;
$tmp = array();
$inserted = false;
for($i = 0; $i < $c; $i++){
if(!$inserted && $arr[$i] <= $elem){
$tmp[] = $elem;
$inserted = true;
}
$tmp[] = $arr[$i];
if($lastIndex == $i && !$inserted) $tmp[] = $elem;
}
$arr = $tmp;
return true;
}
// new element inserted at the end of array
// and moved up until correct place
function insert2(&$arr, $elem){
$c = count($arr);
array_push($arr, $elem);
for($i = $c; $i > 0; $i--){
if($arr[$i - 1] >= $arr[$i]) break;
$tmp = $arr[$i - 1];
$arr[$i - 1] = $arr[$i];
$arr[$i] = $tmp;
}
return true;
}
// binary search for correct place + array_splice() to insert element
function insert3(&$arr, $elem){
$startIndex = 0;
$stopIndex = count($arr) - 1;
$middle = 0;
while($startIndex < $stopIndex){
$middle = ceil(($stopIndex + $startIndex) / 2);
if($elem > $arr[$middle]){
$stopIndex = $middle - 1;
}else if($elem <= $arr[$middle]){
$startIndex = $middle;
}
}
$offset = $elem >= $arr[$startIndex] ? $startIndex : $startIndex + 1;
array_splice($arr, $offset, 0, array($elem));
}
// for loop to find correct place + array_splice() to insert
function insert4(&$arr, $elem){
$c = count($arr);
$inserted = false;
for($i = 0; $i < $c; $i++){
if($elem >= $arr[$i]){
array_splice($arr, $i, 0, array($elem));
$inserted = true;
break;
}
}
if(!$inserted) $arr[] = $elem;
return true;
}
/*********************** Speed tests: *************************/
// check if array is sorted descending
function checkIfArrayCorrect($arr, $expectedCount = null){
$c = count($arr);
if(isset($expectedCount) && $c != $expectedCount) return false;
$correct = true;
for($i = 0; $i < $c - 1; $i++){
if(!isset($arr[$i + 1]) || $arr[$i] < $arr[$i + 1]){
$correct = false;
break;
}
}
return $correct;
}
// claculates microtimetime diff
function timeDiff($startTime){
$diff = microtime(true) - $startTime;
return $diff;
}
// prints formatted execution time info
function showTime($func, $time){
printf("Execution time of %s(): %01.7f s\n", $func, $time);
}
// generated elements num
$elementsNum = 10000;
// generate starting point
$start = 300000;
// generated elements random range 1 - $dev
$dev = 50;
echo "Generating array with descending order, $elementsNum elements, begining from $start\n";
$startTime = microtime(true);
$arr = generateSortedArray($start, $elementsNum, $dev);
showTime('generateSortedArray', timeDiff($startTime));
$step = 2;
echo "Generating second array using range range(), $elementsNum elements, begining from $start, step $step\n";
$startTime = microtime(true);
$arr2 = range($start, $start - $elementsNum * $step, $step);
showTime('range', timeDiff($startTime));
echo "Checking if array is correct\n";
$startTime = microtime(true);
$sorted = checkIfArrayCorrect($arr, $elementsNum);
showTime('checkIfArrayCorrect', timeDiff($startTime));
if(!$sorted) die("Array is not in descending order!\n");
echo "Array OK\n";
$toInsert = array();
// number of elements to insert from every range
$randElementNum = 20;
// some ranges of elements to insert near begining, middle and end of generated array
// start value => end value
$ranges = array(
300000 => 280000,
160000 => 140000,
30000 => 0,
);
foreach($ranges as $from => $to){
$values = array();
echo "Generating $randElementNum random elements from range [$from - $to] to insert\n";
while(count($values) < $randElementNum){
$values[mt_rand($from, $to)] = 1;
}
$toInsert = array_merge($toInsert, array_keys($values));
}
// some elements to insert on begining and end of array
array_push($toInsert, 310000);
array_push($toInsert, -1000);
echo "Generated elements: \n";
for($i = 0; $i < count($toInsert); $i++){
if($i > 0 && $i % 5 == 0) echo "\n";
printf("%8d, ", $toInsert[$i]);
if($i == count($toInsert) - 1) echo "\n";
}
// functions to test
$toTest = array('insert1' => null, 'insert2' => null, 'insert3' => null, 'insert4' => null);
foreach($toTest as $func => &$time){
echo "\n\n================== Testing speed of $func() ======================\n\n";
$tmpArr = $arr;
$startTime = microtime(true);
for($i = 0; $i < count($toInsert); $i++){
$func($tmpArr, $toInsert[$i]);
}
$time = timeDiff($startTime, 'checkIfArraySorted');
showTime($func, $time);
echo "Checking if after using $func() array is still correct: \n";
if(!checkIfArrayCorrect($tmpArr, count($arr) + count($toInsert))){
echo "Array INCORRECT!\n\n";
}else{
echo "Array OK!\n\n";
}
echo "Few elements from begining of array:\n";
print_r(array_slice($tmpArr, 0, 5));
echo "Few elements from end of array:\n";
print_r(array_slice($tmpArr, -5));
//echo "\n================== Finished testing $func() ======================\n\n";
}
echo "\n\n================== Functions time summary ======================\n\n";
print_r($toTest);
Results can be found here: http://ideone.com/1xQ3T
Unfortunately I was rated only 13 points out of 30 for this task (don't know how it was calculated or what exactly was taken in account). I can only assume that's because there are better ways to insert new element into sorted array in PHP. I'm searching this topic for some time now but couldn't find anything good. Maby you know of better approach or some articles about that topic?
Btw on my localhost (PHP 5.3.6-13ubuntu3.6 with Suhosin-Patch, AMD Athlon(tm) II X4 620) insert2() is fastest, but on ideone (PHP 5.2.11) insert3() is fastest.
Any ideas why? I suppose that array_splice() is tuned up somehow :).
//EDIT
Yesterday I thought about it again, and figured out the better way to do inserts. If you only need sorted structure and a way to iterate over it and your primary concern is the speed of insert operation, than the best choise would be using SplMaxHeap class. In SplMaxHeap class inserts are damn fast :) I've modified my script to show how fast inserts are. Code is here: http://ideone.com/vfX98 (ideone has php 5.2 so there won't be SplMaxHeap class)
On my localhost I get results like that:
================== Functions time summary ======================
insert1() => 0.5983521938
insert2() => 0.2605950832
insert3() => 0.3288729191
insert4() => 0.3288729191
SplMaxHeap::insert() => 0.0000801086
It may just be me, but maybe they were looking for readability and maintainability as well?
I mean, you're naming your variables $arr, and $c and $middle, without even bothering to place proper documentation.
Example:
/**
* generateSortedArray() Function to generate a descending sorted array
*
* #param int $start Beginning with this number
* #param int $elementsNum Number of elements in array
* #param int $dev Maximum difference between elements
* #return array Sorted descending array.
*/
function generateSortedArray($start = 300000, $elementsNum = 10000, $dev = 30) {
$arr = array(); #Variable definition
for ($i = 1; $i <= $elementsNum; $i++) {
$rand = mt_rand(1, $dev); #Generate a random number
$start -= $rand; #Substract from initial value
$arr[] = $start; #Push to array
}
return $arr;
}

Writing merge sort in PHP

I have tried to write a basic merge sort in PHP involving a small array, yet the problem is it takes about a minute or so to execute, and returns:
Fatal error: Allowed memory size of 536870912 bytes exhausted (tried
to allocate 35 bytes) in /Users/web/www/merge.php on line 39
Does anyone have an idea where the code might be going wrong (if at all)? I've been staring at this for a good hour now.
<?php
$array = array(8,1,2,5,6,7);
print_array($array);
merge_sort($array);
print_array($array);
function merge_sort(&$list){
if( count($list) <= 1 ){
return $list;
}
$left = array();
$right = array();
$middle = (int) ( count($list)/2 );
// Make left
for( $i=0; $i < $middle; $i++ ){
$left[] = $list[$i];
}
// Make right
for( $i = $middle; $i < count($list); $i++ ){
$right[] = $list[$i];
}
// Merge sort left & right
merge_sort($left);
merge_sort($right);
// Merge left & right
return merge($left, $right);
}
function merge(&$left, &$right){
$result = array();
while(count($left) > 0 || count(right) > 0){
if(count($left) > 0 && count(right) > 0){
if($left[0] <= $right[0]){
$result[] = array_shift($left);
} else {
$result[] = array_shift($right);
}
} elseif (count($left) > 0){
$result[] = array_shift($left);
} elseif (count($right) > 0){
$result[] = array_shift($right);
}
}
print_array($result);exit;
return $result;
}
function print_array($array){
echo "<pre>";
print_r($array);
echo "<br/>";
echo "</pre>";
}
?>
In your merge function, you call count on right instead of $right. PHP assumes this is a string constant (at least in 5.3.9) and when casted into an array that always has one element. So count(right) is always one, and you never exit the first merge.
Try this approach. Instead of shifting it, slice.
Also, for in while loop for the merge function, you need to do an and && comparison instead
of ||
function mergeSort($array)
{
if(count($array) == 1 )
{
return $array;
}
$mid = count($array) / 2;
$left = array_slice($array, 0, $mid);
$right = array_slice($array, $mid);
$left = mergeSort($left);
$right = mergeSort($right);
return merge($left, $right);
}
function merge($left, $right)
{
$res = array();
while (count($left) > 0 && count($right) > 0)
{
if($left[0] > $right[0])
{
$res[] = $right[0];
$right = array_slice($right , 1);
}
else
{
$res[] = $left[0];
$left = array_slice($left, 1);
}
}
while (count($left) > 0)
{
$res[] = $left[0];
$left = array_slice($left, 1);
}
while (count($right) > 0)
{
$res[] = $right[0];
$right = array_slice($right, 1);
}
return $res;
}
Have a look at this, the algorithm is already implemented, using array_push and array splice instead of just array_shift.
http://www.codecodex.com/wiki/Merge_sort#PHP
I implement merge sort this way
function mergeSort($Array)
{
$len = count($Array);
if($len==1){
return $Array;
}
$mid = (int)$len / 2;
$left = mergeSort(array_slice($Array, 0, $mid));
$right = mergeSort(array_slice($Array, $mid));
return merge($left, $right);
}
function merge($left, $right)
{
$combined = [];
$totalLeft = count($left);
$totalRight = count($right);
$rightIndex = $leftIndex=0;
while ($leftIndex < $totalLeft && $rightIndex < $totalRight) {
if ($left[$leftIndex] > $right[$rightIndex]) {
$combined[]=$right[$rightIndex];
$rightIndex++;
}else {
$combined[] =$left[$leftIndex];
$leftIndex++;
}
}
while($leftIndex<$totalLeft){
$combined[]=$left[$leftIndex];
$leftIndex++;
}
while ($rightIndex<$totalRight){
$combined[] =$right[$rightIndex];
$rightIndex++;
}
return $combined;
}
Here is the class in PHP to implement the Merge Sort -
<?php
class mergeSort{
public $arr;
public function __construct($arr){
$this->arr = $arr;
}
public function mSort($l,$r){
if($l===null || $r===null){
return false;
}
if ($l < $r)
{
// Same as ($l+$r)/2, but avoids overflow for large $l and $r
$m = $l+floor(($r-$l)/2);
// Sort first and second halves
$this->mSort($l, $m);
$this->mSort($m+1, $r);
$this->merge($l, $m, $r);
}
}
// Merges two subarrays of $this->arr[]. First subarray is $this->arr[$l..$m]. Second subarray is $this->arr[$m+1..$r]
public function merge($l, $m, $r)
{
if($l===null || $m===null || $r===null){
return false;
}
$n1 = $m - $l + 1;
$n2 = $r - $m;
/* create temp arrays */
$L=array();
$R=array();
/* Copy data to temp arrays $L[] and $R[] */
for ($i = 0; $i < $n1; $i++)
$L[$i] = $this->arr[$l + $i];
for ($j = 0; $j < $n2; $j++)
$R[$j] = $this->arr[$m + 1+ $j];
/* Merge the temp arrays back into $this->arr[$l..$r]*/
$i = 0; // Initial index of first subarray
$j = 0; // Initial index of second subarray
$k = $l; // Initial index of merged subarray
while ($i < $n1 && $j < $n2)
{
if($L[$i] <= $R[$j])
{
$this->arr[$k] = $L[$i];
$i++;
}
else
{
$this->arr[$k] = $R[$j];
$j++;
}
$k++;
}
/* Copy the remaining elements of $L[], if there are any */
while($i < $n1)
{
$this->arr[$k] = $L[$i];
$i++;
$k++;
}
/* Copy the remaining elements of $R[], if there are any */
while($j < $n2)
{
$this->arr[$k] = $R[$j];
$j++;
$k++;
}
}
}
$arr = array(38, 27, 43, 5, 9, 91, 12);
$obj = new mergeSort($arr);
$obj->mSort(0,6);
print_r($obj->arr);
?>
I was looking for a optimized Mergesort algorithm in PHP. There are 5 algorithms in the answers, so I tested those, and mine too. Using PHP 7.2.7, these are the times:
Sorting 1000 random numbers:
Avanche 1 0.0396 seconds
Avanche 2 0.0347 seconds
Kartik 0.0291 seconds
Kripa 0.0282 seconds
Samuel 0.0247 seconds
Mine 0.0144 seconds
Sorting 10 random numbers:
Avanche 1 0.000222 seconds
Kartik 0.000216 seconds
Kripa 0.000159 seconds
Avanche 2 0.000144 seconds
Samuel 0.000128 seconds
Mine 0.000098 seconds
So, although I encourage to whom read it to make it faster (that was I was looking for, and I believe it can be done), I let you my implementation too, cause seems to be faster than the other answers:
//This function needs start and end limits
function mergeSortRec(&$a,$start,$end){
if($start<$end){
$center=($start+$end)>>1; //Binary right shift is like divide by 2
mergeSortRec($a, $start, $center);
mergeSortRec($a, $center+1, $end);
//Mixing the 2 halfs
$aux=array();
$left=$start; $right=$center;
//Main loop
while($left<$center && $right<=$end){
if($a[$left]<$a[$right]){
$aux[]=$a[$left++];
}else{
$aux[]=$a[$right++];
}
}
//Copy the rest of the first half
while($left<$center) $aux[]=$a[$left++];
//Copy the rest of the second half
while($right<=$end) $aux[]=$a[$right++];
//Copy the aux array to the main array
foreach($aux as $v) $a[$start++]=$v;
}
}
//This is the function easier to call
function mergeSort(&$a) {
mergeSortRec($a,0,count($a)-1);
}
If you post a new answer, let me a comment to test it and add it.
Edit: I did some new optimizations, for those looking for a better implementation.
Your merge sort accepts a list by reference
function merge_sort(&$list)
So you need to assign it the new merged and sorted list. So instead of
return merge($left, $right);
do
$list = $this->merge($left, $right);
That should do it, just remove the exit and fix the count variable
MergeSort in PHP
<?php
class Solution
{
function mergeSort(&$arr)
{
if(count($arr) > 1) {
$mid = floor(count($arr)/2);
$left = array_slice($arr, 0, $mid);
$right = array_slice($arr, $mid);
$this->mergeSort($left);
$this->mergeSort($right);
// Merge the results.
$i = $j = $k = 0;
while(($i < count($left)) && ($j < count($right))) {
if($left[$i] < $right[$j]) {
$arr[$k] = $left[$i];
$i++;
} else {
$arr[$k] = $right[$j];
$j++;
}
$k++;
}
while($i < count($left)) {
$arr[$k] = $left[$i];
$i++;
$k++;
}
while($j < count($right)) {
$arr[$k] = $right[$j];
$j++;
$k++;
}
}
}
}
$s = new Solution();
$tmp = [12, 7, 11, 13, 5, 6, 7];
$s->mergeSort($tmp);
print_r($tmp);

Categories