php implementation of finding cycle in minimum swaps - php

find out minimum number of adjacent swaps require to make the array sorted.
the approach I use is finding a repeated cycle within my array for example 3->1->4->2 is cycle of 4 so minimum swap of 3 with input of [3,1,4,2,5] to make it sorted [1,2,3,4,5]
I have three arrays, $old contains unsorted array, $arr is sorted, and $bool counts whether the position is visited or not
$old = $arr;
quicksort($arr, 0, count($arr) - 1);
$bool = array_fill(0, count($arr), false);
$count = 0;
$circle = 0;
for ($i = 0; $i < count($bool); $i++){
if ($arr[$i] !== $old[$i]) {
$circle ++;
$where = array_search($old[$i], $arr);
$bool[$i] = true;
if ($bool[$where]) {
$count += $circle;
$circle = 0;
}
}
}
return $count;
the idea is skipping element that is already in right index,
and mark element that is not, keeping count of cycle (i used $circle), once a right position is already marked, i conclude that a cycle is reached,
but this logic is not behaving what I expected, need some help.

I think what you are after is something like this, however I might be wrong.
I tried to keep it simple with a lot of loops to make it work, and explained the different steps in the code
<?php
$minSize = 3;
$arr = [3,1,2,6,9,3,5,2,6,9,3,5,1,8,4,6,6,9,3,0];
$repeats = [];
//outer loop
for($i = 0; $i < count($arr); $i++) {
$currValue = $arr[$i];
//get all the keys for where the value is the same as $currValue
//this gives a starting position to test our matching sets
$matches = array_keys($arr, $currValue);
//match loop
for($j = 0; $j < count($matches); $j++) {
$leftIndex = $currValue; //this needs to be reset for every set
$matchIndex = $matches[$j];
//now we do want matches only when the index is different, always it will always match
if($leftIndex === $matchIndex) {
continue;
}
$subsequentMatch = 0;
//isset so we don't go out of bounds of our array
while(isset($arr[$leftIndex], $arr[$matchIndex]) && $arr[$leftIndex] === $arr[$matchIndex]) {
$subsequentMatch++;
$leftIndex++;
$matchIndex++;
}
if($subsequentMatch >= $minSize) {
$repeats[] = array_slice($arr, $currValue, $subsequentMatch) ;
}
}
}
print_r($repeats);
See an example here : https://3v4l.org/cBWQ5
you could improve on this by doing
for($i = 0; $i < (count($arr) - $minSize ); $i++) {
because if the set is under $minSize it will never be added to the repeats
EDIT: based on comments, OP might be looking for this ?
<?php
$arr = [3,1,2,6,9,3,5,2,6,9,3,5,1,8,4,6,6,9,3,0];
$swapCount = 0;
usort($arr, function ($a, $b) use (&$swapCount) {
if($a === $b) {
return 0;
}
$swapCount++;
return $a <=> $b;
});
echo 'Arr Count: ' . count($arr) . PHP_EOL;
echo 'Swaps performed: ' . $swapCount;
This is a simple sort, I suppose you could write a usort and count with each different sort mechanism (the list is endless), and then find out the min value of that
list of sorting mechanism off the top of my head
Insert Sort
Bubble Sort
Quick Sort
Counting Sort
Comb Sort
Heap Sort
Merge Sort
Shell Sort
Odd Even Sort
You can google on how to implement these in PHP, there are probably more ways too.

Related

PHP, array count, two foreach combination

I am solving this not understandable script, I even don't know where from to start. Maybe someone will help me.
I got two arrays example:
$a1 = array(1,2,3 ...);
$a2 = array(4,5,6 ...);
what I need is that first array will divide all values by itsel
for this I would like to get script which will work like this (array values can be savet to the same name because will be anyway continue with them, can be new variable, in this example I use new). In the same way variable can be devidet by itself to get 1 but prefer not to. Example of count.
$b1 = array([0]/[1], [0]/[2], [0]/[3], [1]/[0], [1]/[2], [1]/[3], [2]/[0], [2]/[1], [2]/[3], [3]/[0], [3]/[1], [3]/[2]);
$b2 = array( *** The same like $b1 ***);
On the end will go foreach to write values to table, this I solved already
echo "<table><tr>";
foreach($b1 as $key1 => $val1){
foreach($b2 as $key2 =>$val2){
echo "<td>".$key1.$key2."<br/>".val1*val2."</td>";
}
echo "</tr><tr>";
}
echo "</tr></table>"
Anyone can give me a help to this issue?
To do the division of each element is relatively simple. You do it with 2 for loops. The one thing you have to bear in mind, however, is avoiding division by 0. Do you have elements that could be 0? If so, do you want the division result to be 0, or the same as the numerator? the code I show below assumes the latter:
function getDivided($array) {
$length = count($array);
$return = [];
for ($i = 0; $i < $length; $i++) {
for ($j = 0; $j < $length; $j++) {
if ($i === $j) continue;
//the following assumes that the values in the array are never < 1 Change this according to your needs
$return[] = $array[$i] / max($array[$j], 1);
//Another option is to use a conditional
$return[] = $array[$j] === 0 ? $array[$i] : $array[$i] / $array[$j];
}
}
return $return;
}
$b1 = getDivided($a1);
$b2 = getDivided($a2);

Using an index twice in PHP

I am trying to use a for loop where it looks through an array and tries to make sure the same element is not used twice. For example, if $r or the random variable is assigned the number "3", my final array list will find the value associated with wordList[3] and add it. When the loop runs again, I don't want $r to use 3 again. Example output: 122234, where I would want something along the lines of 132456. Thanks in advance for the help.
for($i = 0; $i < $numWords; $i++){
$r = rand(0, $numWords);
$arrayTrack[$i] == $r;
$wordList[$r] = $finalArray[$i];
for($j = 0; $j <= $i; $j++){
if($arrayTrack[$j] == $r){
# Not sure what to do here. If $r is 9 once, I do not want it to be 9 again.
# I wrote this so that $r will never repeat itself
break;
}
}
Edited for clarity.
Pretty sure you are over complicating things. Try this, using array_rand():
$final_array = array();
$rand_keys = array_rand($wordList, $numWords);
foreach ($rand_keys as $key) {
$final_array[] = $wordList[$key];
}
If $numWords is 9, this will give you 9 random, unique elements from $wordList.
See demo
$range = range(0, $numWords - 1); // may be without -1, it depends..
shuffle($range);
for($i = 0; $i < $numWords; $i++) {
$r = array_pop($range);
$wordList[$r] = $finalArray[$i];
}
I do not know why you want it.. may be it is easier to shuffle($finalArray);??
So ideally "abcdefghi" in some random order.
$letters = str_split('abcdefghi');
shuffle($letters);
var_dump($letters);
ps: if you have hardcoded array $wordList and you want to take first $n elements of it and shuffle then (if this is not an associative array and you do not care about the keys)
$newArray = array_slice($wordList, 0, $n);
shuffle($newArray);
var_dump($newArray);
You can try array_rand and unset
For example:
$array = array('one','two','free','four','five');
$count = count($array);
for($i=0;$i<$count;$i++)
{
$b = array_rand($array);
echo $array[$b].'<br />';
unset($array[$b]);
}
after you have brought the data in the array, you purify and simultaneously removing the memory array
Ok... I have NO idea why you are trying to use so many variables with this.
I certainly, have no clue what you were using $arrayTrack for.
There is a very good chance I am mis-understanding all of this though.
<?php
$numWords=10;
$wordList=array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
$finalArray=array();
for ($i=0; $i<$numWords; $i++) {
start:
$r=rand(0,$numWords);
$wordChoice=$wordList[$r];
foreach ($finalArray as $word) {
if ($word==$wordChoice) goto start;
}
$finalArray[]=$wordChoice;
}
echo "Result: ".implode(',',$finalArray)."\n";

Given an array of integers, what's the most efficient way to get the number of other integers in the array within n?

Given the following array:
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
And assuming $n = 2, what is the most efficient way to get a count of each value in the array within $n of each value?
For example, 6 has 3 other values within $n: 5,7,7.
Ultimately I'd like a corresponding array with simply the counts within $n, like so:
// 0,0,1,2,2,5,6,7,7,9,10,10 // $arr, so you can see it lined up
$count_arr = array(4,4,4,4,4,3,3,4,4,4, 2, 2);
Is a simple foreach loop the way to go? CodePad Link
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
$n = 2;
$count_arr = array();
foreach ($arr as $v) {
$range = range(($v-$n),($v+$n)); // simple range between lower and upper bound
$count = count(array_intersect($arr,$range)); // count intersect array
$count_arr[] = $count-1; // subtract 1 so you don't count itself
}
print_r($arr);
print_r($count_arr);
My last answer was written without fully groking the problem...
Try sorting the array, before processing it, and leverage that when you run through it. This has a better runtime complexity.
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
asort($arr);
$n = 2;
$cnt = count($arr);
$counts = array_pad(array(), $cnt, 0);
for ($x=0; $x<$cnt; $x++) {
$low = $x - 1;
$lower_range_bound = $arr[$x]-$n;
while($low >= 0 && ($arr[$low] >= $lower_range_bound)) {
$counts[$x]++;
$low--;
}
$high = $x + 1;
$upper_range_bound = $arr[$x]+$n;
while($high < $cnt && $arr[$high] <= $upper_range_bound) {
$counts[$x]++;
$high++;
}
}
print_r($arr);
print_r($counts);
Play with it here: http://codepad.org/JXlZNCxW

How to sort it better?

I have 2 arrays, both are multidimensional with same number of elements and same values, which are on different positions (those values are actually ID-s from my database, so one ID appears only once). How can I sort second array with values which are in first array?
For example - if first array looks like:
$array1[0][0] = 1;
$array1[0][x] = it doesn't matter what's here
$array1[1][0] = 4;
$array1[1][x] = it doesn't matter what's here
$array1[2][0] = 3;
$array1[2][x] = it doesn't matter what's here
...
how to sort second array so it would have same values as array1 on indexes [0][0], [1][0], [2][0], etc.
How I could solve problem is:
$i=0
while ($i < (count($array1)-2)){ // * check down
$find_id = $array1[$i][0];
// here I need to search for index of that ID in other array
$position = give_index($find_id, $array2);
// swapping positions
$temp = array2[$i][0];
$array2[$i][0] = $array2[$position][0];
$array2[$position][0] = $temp;
// increasing counter
i++;
}
function give_index($needle, $haystack){
for ($j = 0, $l = count($haystack); $j < $l; ++$j) {
if (in_array($needle, $haystack[$j][0])) return $j;
}
return false;
}
*There is only -2 because indexes start from 0 and also for the last element you don't need to check since it would be automatically sorted by last iteration of while-loop.
I don't find this solution good as I think that this is quite simple issue (maybe it's not even correct). Is there easier way in PHP that I'm missing?
This is the most efficient way I can think of:
function swap(&$a, &$b) {
$t = $a;
$a = $b;
$b = $t;
}
function find_index($id, $array, $from = 0) {
$index = false;
for ($i = $from, $c = count($array); $i < $c; $i++) {
if ($array[$i][0] == $id) {
$index = $i;
break;
}
}
return $index;
}
for ($i = 0, $c = count($array1); $i < ($c - 2); $i++) {
if ($array1[$i][0] != $array2[$i][0]) {
$fi = find_index($array1[$i][0], $array2, $i);
swap($array2[$i][0], $array2[$fi][0]);
}
}
What changes from yours?
I've defined a swap() function in order to swap any variable. That doesn't cost anything and makes everything look nicer. Also you can reuse that function later if you need to.
In the find_index (give_index in your code) we stop the loop once we find the correct index. Also we avoid the cost of an in_array function call.
We modified the find_index function to start only from the part of the array we haven't checked yet. Leading to a way more efficient way of scan the array.
In the for loop (a while loop was just wrong there) we stored the count of the array once, avoiding multiple calls.
Also we swap the $array2 values only if they are in the wrong place.
Other improvements
If you know anything else of the $array2 array you can make this even more performant. For example if you know that indexes are alternated like in $array1 you can change the main for loop from:
for ($i = 0, $c = count($array1); $i < ($c - 2); $i++) {
to
for ($i = 0, $c = count($array1); $i < ($c - 2); $i+2) {
(notice the $i+2 at the end) And you could do that in the find_index function as well.
Look into usort (http://php.net/manual/en/function.usort.php).
It provides a simple way to sort arrays using a user provided comparison function.

Reorder elements in array evenly

Say i have:
$array = (1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
What I'm trying to achive is to reorder elements evenly in it.
PHP's function shuffle() don't fits here, because i want some distance between same digits. So 1's has to be somewhere in the beginning of array, in the middle and in the end too.
I google about Fisher-Yates_shuffle algorithm, but it seems to work exactly like shuffle().
Thanks in advance!
I think this is close to what you ask: A constant, reasonably even distribution of the items in an array.
// The input array. 0s are regarded as blanks.
$array = array(1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
// Count the times each item occurs. PHP will probably have a function for that, but I don't know.
$counter = array();
foreach ($array as $item)
{
// Zeros are infill. Don't process them now, only process the other numbers and
// the zeros will occupy the remaining space.
if ($item === 0)
continue;
if (!array_key_exists($item, $counter))
$counter[$item] = 0;
$counter[$item]++;
}
// Reverse sort by quantity. This results in the best distribution.
arsort($counter);
// Pre-fill a new array with zeros.
$resultCount = count($array);
$result = array_fill(0, $resultCount, 0);
// Distribute the items in the array, depending on the number of times they occur.
foreach ($counter as $item => $count)
{
// Determine the division for this item, based on its count.
$step = $resultCount / $count;
// Add the item the right number of times.
for ($i = 0; $i < $count; $i++)
{
// Start with the index closest to the preferred one (based on the calculated step).
$index = 0;
$startIndex = (int)($step * $i);
// Count up until a right index is found.
for ($index = $startIndex; $index < $resultCount; $index++)
{
if ($result[$index] === 0)
{
$result[$index] = $item;
break;
}
}
// If no proper index was found, count fown from the starting index.
if ($index === $resultCount)
{
for ($index = $startIndex; $index >= 0; $index--)
{
if ($result[$index] === 0)
{
$result[$index] = $item;
break;
}
}
}
// Still no proper index found, that shouldn't be possible. There's always room.
if ($index === -1)
{
throw new Exception('This cannot not happen');
}
}
}
var_dump($result);
For array:
1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
It returns:
3,2,1,0,3,0,0,0,3,0,2,1,3,0,0,0,3,0,0,0,0,3,2,1,0,3,0,0,0,3,0,2,1,3,0,0,0,3,0,0,0,0
For array:
1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0
It returns:
4,4,3,4,3,4,2,4,3,4,2,4,3,4,1,4,3,4,1,4,0,4,4,3,4,3,4,2,4,3,4,2,4,3,4,1,4,3,4,1,4,0
Which I think is a neat distribution. Thanks to datdo for the idea of sorting the intermediate array.

Categories