How to iterate a loop of array circularly in php? - php

Suppose i have a number let's say 5. Now lets assume that there are 5 members. Now each member started to count 1 to 2. Those member who get 2nd number leaves and then again count start from the next member. so at last in this scenario the 3rd member stays at last.
So i tried to implement like this. First of assign members as array $v.
for($i=1 ; $i<=5 ; $i++)
{
$v[] = $i;
}
$v1 = array_flip($v);
for($i=0 ; $i<=5 ; $i += 2 )
{
unset($v1[$i]);
}
echo "<pre>";
print_r($v1);
output
Array
(
[1] => 0
[3] => 2
[5] => 4
)
Now i want to count the numbers from key 5(5th member) to again 1(1st member) and so on.
so at last key 3(3rd member) left.
I want to print the last member that left.
How can i achieve this?
I you can't understand then look at this
Survival Strategy

Here's an Object-Oriented solution with an easy-to-follow reduce method and multiple examples.
class CountByTwoArrayReducer {
public function __construct($array) {
$this->array = $array;
$this->size = count($array);
}
public function reduce() {
$this->initialize();
while($this->hasMultipleItems()) {
$this->next();
$this->removeCurrentItem();
$this->next();
}
return $this->finalItem();
}
protected function initialize() {
$this->current = 1;
$this->removed = array();
$this->remaining = $this->size;
}
protected function hasMultipleItems() {
return ($this->remaining > 1);
}
protected function next($start = null) {
$next = ($start === null) ? $this->current : $start;
do {
$next++;
} while(isset($this->removed[$next]));
if($next > $this->size)
$this->next(0);
else
$this->current = $next;
}
protected function removeCurrentItem() {
$this->removed[$this->current] = 1;
$this->remaining--;
}
protected function finalItem() {
return $this->array[$this->current - 1];
}
}
$examples = array(
array('A', 'B', 'C', 'D', 'E'),
range(1, 100),
range(1, 1000),
range(1, 10000)
);
foreach($examples as $example) {
$start = microtime(true);
$reducer = new CountByTwoArrayReducer($example);
$result = $reducer->reduce();
$time = microtime(true) - $start;
echo "Found {$result} in {$time} seconds.\n";
}

This will remove every other item from the array until there is only a single item left.
$members = range(1, 5);
$i = 0;
while(count($members) > 1) {
$i++;
if($i == count($members)) $i = 0;
unset($members[$i]);
$members = array_values($members);
if($i == count($members)) $i = 0;
}
echo $members[0];

Hmm, I can recommend two functions:
http://php.net/manual/en/function.array-keys.php
This would re-index you array, with indexes: 0, 1, 2
http://php.net/manual/en/control-structures.foreach.php
With this you can go through any array by:
foreach($v1 as $key=>$value) { ...select maximum, etc. ... }

<?php
function build_thieves($thieves)
{
return range(1, $thieves);
}
function kill_thief(&$cave)
{
if(sizeof($cave)==1)
{
$thief=array_slice($cave, 0, 1);
echo $thief.' survived';
return false;
}
$thief=array_slice($cave, 0, 1);
array_push($cave, $thief);
$thief=array_slice($cave, 0, 1);
echo $thief.' killed';
return true;
}
$cave=build_thieves(5);
$got_data=true;
while($got_data)
{
$got_data=kill_thief($cave);
}
Adjusted to every 2nd, not every 3rd. And starting from 1 not 0

This answer is a bit more complex, but it is much more efficient. It doesn't create an array of items and then remove them. It starts with a value (e.g. 1) and calculates the next item that has not been removed yet. Then, it flags it as removed. If you actually have an array of items, the index of the final item will be $current - 1. The example below uses values 1 through 10,000. On my machine, it takes just over 0.05 seconds.
define('SIZE', 10000);
/**
* Helper function to return the next value
*/
function get_next($current, &$removed) {
$next = $current;
do {
$next++;
} while(isset($removed[$next]));
return ($next > SIZE) ? get_next(0, $removed) : $next;
}
$current = 1;
$removed = array();
$remaining = SIZE;
$start_time = microtime(true);
while($remaining > 1) {
$current = get_next($current, $removed);
$removed[$current] = 1;
$remaining = SIZE - count($removed);
$current = get_next($current, $removed);
}
$total_time = microtime(true) - $start_time;
echo "Processed " . SIZE . " items\n";
echo "Winning item: {$current}\n";
echo "Total time: {$total_time} seconds\n";

Related

PHP: How to get combination with the most matches of the sub-arrays?

I have an $myArray with sub-arrays which always contain 5 numbers - the numbers are sorted by size and can not be repeated in the sub-array, but there can be more "identical" sub-arrays (sub-arrays with the same numbers) in $myArray.
$myArray = array(
array(1,2,3,4,5),
array(5,6,10,18,20),
array(1,2,3,4,5),
array(2,3,4,5,9),
array(1,2,3,7,9),
array(1,3,4,5,7),
array(2,3,4,7,9),
array(2,4,5,10,29),
array(1,8,10,11,15) // etc.
);
How can I get an combination (array) of $n numbers where this combination (or rather the 5-number combinations generated from this $n-number combination) will match the most of the sub-arrays of $myArray?
Example: the desired result for $n=7 for the $myArray would be array(1,2,3,4,5,7,9) because there are twenty one 5-number combinations in total derived from this result:
1,2,3,4,5
1,2,3,4,7
1,2,3,4,9
//... and so on
and these combinations would match almost all sub-arrays (only second and last two sub-arrays are out of range).
I have tried counting with array_count_values() but simple frequency of all numbers doesn't work in this case...
class Combinations implements Iterator
{
protected $c = null;
protected $s = null;
protected $n = 0;
protected $k = 0;
protected $pos = 0;
function __construct($s, $k) {
if(is_array($s)) {
$this->s = array_values($s);
$this->n = count($this->s);
} else {
$this->s = (string) $s;
$this->n = strlen($this->s);
}
$this->k = $k;
$this->rewind();
}
function key() {
return $this->pos;
}
function current() {
$r = array();
for($i = 0; $i < $this->k; $i++)
$r[] = $this->s[$this->c[$i]];
return is_array($this->s) ? $r : implode('', $r);
}
function next() {
if($this->_next())
$this->pos++;
else
$this->pos = -1;
}
function rewind() {
$this->c = range(0, $this->k);
$this->pos = 0;
}
function valid() {
return $this->pos >= 0;
}
//
protected function _next() {
$i = $this->k - 1;
while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
$i--;
if($i < 0)
return false;
$this->c[$i]++;
while($i++ < $this->k - 1)
$this->c[$i] = $this->c[$i - 1] + 1;
return true;
}
}
$tickets = array(
array(1,2,3,4,5),
array(5,6,10,18,20),
array(1,2,3,4,5),
array(2,3,4,5,9),
array(1,2,3,7,9),
array(1,3,4,5,7),
array(2,3,4,7,9),
array(2,4,5,10,29),
array(1,8,10,11,15) // etc.
);
// first we need to find all numbers that are actually in one of the arrays.
foreach($tickets as $anArray) {
foreach($anArray as $aNumberUsed){
$numbersUsed[$aNumberUsed] = $aNumberUsed;
}
}
// next we assign the number of integers in the set we are looking for.
$r = 7;
// next we run the above class on our array (which gets us all of the possible combinations of these numbers).
foreach(new Combinations($numbersUsed, 7) as $comboKey => $substring){
$comboList[$comboKey] = $substring;
$countWins = 0;
// here we loop through all of the 5 number arrays, and flag any array who has all the variables in this iteration of the possible numbers. There are cleaner ways to do this, but this is easy to understand.
foreach($tickets as $valueList) {
$countNumbersFound = 0;
foreach($valueList as $value) {
if(in_array($value, $substring)) {
$countNumbersFound++;
}
}
if($countNumbersFound == 5) {
$countWins++;
}
}
$foundCount[$comboKey] = $countWins;
}
$bigly = max($foundCount);
$key = array_search($bigly, $foundCount);
foreach($comboList[$key] as $wellDone) {
echo "$wellDone ,";
}
The class is blatantly stolen from here: http://www.developerfiles.com/combinations-in-php/
Everything after the class is original. I don't believe in reinventing the wheel.

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.

Inefficient rotating of an array

I'm trying to make a function that is able to rotate through an array a given amount of times and then return the first index. But what I have is really slow and clunky. Take a look:
<?php
/**
* Get the current userid
* #return integer
*/
public function getCurrentUser( DateTime $startDate, DateInterval $interval, DateTime $endDate, $currentUser, $users, $rotating )
{
if ($rotating == 0)
{
return $currentUser;
}
$usrArray = array();
$dateRange = new DatePeriod( $startDate, $interval, $endDate);
// Push userIds to an array
foreach ($users as $user)
{
$usrArray[] = $user->id;
}
// Get the number of iterations from startDate to endDate
$steps = iterator_count($dateRange);
// Find the initial position of the orignal user
$key = array_search($currentUser, $usrArray);
// Set up the array so index 0 == currentUser
$usr = $usrArray;
array_splice($usr, $key);
$slice = array_slice($usrArray, $key);
$startList = array_merge($slice, $usr);
// Start rotating the array
for ($i=0; $i < $steps; $i++)
{
array_push($startList, array_shift($startList));
}
return $startList[0];
}
Here's an Xdebug profile before the PHP script timed out.
xdebug profile
Is there a better way to figure out who is index 0 after x amount of rotations?
Your array rotation is not that slow but it can be improved .. i believe this is your rotation code
Your code
// Start rotating the array
for ($i=0; $i < $steps; $i++)
{
array_push($startList, array_shift($startList));
}
return $startList[0];
You can remove the loop replace it with mod .. the way you can still get the same results here is the solution:
Solution
return $startList[ $steps % count($startList)];
You would get the same result .
Simple benchmark & Testing
$steps = 10000; <----------------- 10,000 steps
set_time_limit(0);
echo "<pre>";
$file = "log.txt";
// Using your current code
function m1($steps) {
$startList = range("A", "H");
for($i = 0; $i < $steps; $i ++) {
array_push($startList, array_shift($startList));
}
return $startList[0];
}
// Using InfiniteIterator
function m2($steps) {
$startList = range("A", "H");
$n = 0;
foreach ( new InfiniteIterator(new ArrayIterator($startList)) as $l ) {
if ($n == $steps) {
return $l;
break;
}
$n ++;
}
}
// Simple MOD solution
function m3($steps) {
$startList = range("A", "H");
return $startList[ $steps % count($startList)];
}
$result = array('m1' => 0,'m2' => 0,'m3' => 0);
for($i = 0; $i < 1; ++ $i) {
foreach ( array_keys($result) as $key ) {
$alpha = microtime(true);
$key($file);
$result[$key] += microtime(true) - $alpha;
}
}
echo '<pre>';
echo "Single Run\n";
print_r($result);
var_dump(m1($steps),m2($steps),m2($steps));
echo '</pre>';
Output
Single Run
Array
(
[m1] => 0.00012588500976562
[m2] => 0.00021791458129883
[m3] => 7.7962875366211E-5 <----------------- Mod solution fastest
)
string 'A' (length=1) |
string 'A' (length=1) |+------------- They all return same result
string 'A' (length=1) |

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