how to optimise this code? - php

I have a solution to the below problem in PHP.
But it is taking too much time to execute for 10 digit numbers. I want to know where am I going wrong ?
I am new to dynamic programming .Can someone have a look at this ?
Problem
In Byteland they have a very strange monetary system.
Each Bytelandian gold coin has an integer number written on it. A coin n
can be exchanged in a bank into three coins: n/2, n/3 and n/4.
But these numbers are all rounded down (the banks have to make a profit).
You can also sell Bytelandian coins for American dollars. The exchange
rate is 1:1. But you can not buy Bytelandian coins.
You have one gold coin. What is the maximum amount of American dollars
you can get for it?
========================================================
<?php
$maxA=array();
function exchange($money)
{
if($money == 0)
{
return $money;
}
if(isset($maxA[$money]))
{
$temp = $maxA[$money]; // gets the maximum dollars for N
}
else
{
$temp = 0;
}
if($temp == 0)
{
$m = $money/2;
$m = floor($m);
$o = $money/3;
$o = floor($o);
$n = $money/4;
$n = floor($n);
$total = $m+$n+$o;
if(isset($maxA[$m]))
{
$m = $maxA[$m];
}
else
{
$m = exchange($m);
}
if(isset($maxA[$n]))
{
$n = $maxA[$n];
}
else
{
$n = exchange($n);
}
if(isset($maxA[$o]))
{
$o = $maxA[$o];
}
else
{
$o = exchange($o);
}
$temp = max($total,$m+$n+$o,$money);
$maxA[$money]=$temp; //store the value
}
return $temp;
}
$A=array();
while(1)
{
$handle = fopen ("php://stdin","r");
$line = fgets($handle);
if(feof($handle))
{
break;
}
array_push($A,trim($line));
}
$count =count($A);
for($i=0;$i<$count;$i++)
{
$val = exchange($A[$i]);
print "$val \n";
}
?>

Here a reformatted version of the code for the ones (like I) who could understand the above. It doesn't improve anything.
function exchange($money) {
static $maxA = array(0 => 0);
if (isset($maxA[$money])) {
return $money;
}
$m = floor($money/2);
$o = floor($money/3);
$n = floor($money/4);
$total = $m+$n+$o;
if (isset($maxA[$m])) {
$m = $maxA[$m];
} else {
$m = exchange($m);
}
if (isset($maxA[$n])) {
$n = $maxA[$n];
} else {
$n = exchange($n);
}
if (isset($maxA[$o])) {
$o = $maxA[$o];
} else {
$o = exchange($o);
}
return $maxA[$money] = max($total, $m + $n + $o, $money);
}

I still have my code from this problem. But it's in c++. It's by no means the most efficient, but it passed. It might not be too hard to port to php.
#include <cstdio>
#include <queue>
using namespace std;
unsigned long results;
queue to_test;
int main()
{
char tmp_val[30];
unsigned long coin_value = 1;
while (coin_value)
{
scanf("%s", tmp_val);
coin_value = 0;
results = 0;
for (int w = 0; tmp_val[w] != '\0'; w++)
{
coin_value *= 10;
coin_value += tmp_val[w] - 0x30;
}
if (coin_value != 0)
{
to_test.push(coin_value);
while(!to_test.empty())
{
unsigned long tester = to_test.front();
to_test.pop();
unsigned long over2 = tester/2;
unsigned long over3 = tester/3;
unsigned long over4 = tester/4;
if (tester < over2 + over3 + over4)
{
to_test.push(over2);
to_test.push(over3);
to_test.push(over4);
}
else
{
results += tester;
}
}
printf("%lu\n", results);
}
}
}

Related

In which case we can use interpolation algorithm search instead binary search algorithm?

I have two sorted arrays:
$idProducts = [2,9,25,666,1001,1002,1005,2546...n]; //almost 55.000 values
$someIds = [1,9,11,12,99,111,855...n]; //almost 2.500 values
I try make a new array from intersection of idProducts and $someIds.
I applied 3 search algorithms: linear, binary, interpolar searching, but only linear and binary algorithm work properly:
foreach($someIds as $id){
if(interpolation_search($idProducts, $id) >=0){
$newArray[]=$id;
}
}
function interpolation_search($list, $x)
{
$l = 0;
$r = count($list) - 1;
while ($l <= $r) {
if ($list[$l] == $list[$r]) {
if ($list[$l] == $x) {
return $l;
} else {
// not found
return -1;
}
}
$k = ($x - $list[$l])/($list[$r] - $list[$l]);
// not found
if ($k < 0 || $k > 1) {
return -1;
}
$mid = round($l + $k*($r - $l));
if ($x < $list[$mid]) {
$r = $mid - 1;
} else if ($x > $list[$mid]) {
$l = $mid + 1;
} else {
// success!
return $mid;
}
// not found
return -1;
}
}
Anyway the best solution was:
$idProducts = array_flip($idProducts);
foreach ($someIds as $id){
if (isset($idProducts [$id])===true){
$newArray[]=$id;
}
}
But i want know why interpolation doesnt work properly in my case and in which case we can implement interpolation algorithm searching.
Thank you.

PHP find median using heap vs sort

I was looking for a quick way to calculate the median of a list of numbers and came across this:
function array_median($array) {
// perhaps all non numeric values should filtered out of $array here?
$iCount = count($array);
if ($iCount == 0) {
return null;
}
// if we're down here it must mean $array
// has at least 1 item in the array.
$middle_index = floor($iCount / 2);
sort($array, SORT_NUMERIC);
$median = $array[$middle_index]; // assume an odd # of items
// Handle the even case by averaging the middle 2 items
if ($iCount % 2 == 0) {
$median = ($median + $array[$middle_index - 1]) / 2;
}
return $median;
}
This approach using sort() makes sense and is certainly the obvious approach. However, I was curious if a median heap would be faster. What was surprising was that when I implemented a simple median heap it is consistently significantly slower than the above method.
My simple MedianHeap class:
class MedianHeap{
private $lowerHeap;
private $higherHeap;
private $numbers = [];
public function __construct($numbers = null)
{
$this->lowerHeap = new SplMaxHeap();
$this->higherHeap = new SplMinHeap();
if (count($numbers)) {
$this->insertArray($numbers);
}
}
public function insertArray ($numbers) {
foreach($numbers as $number) {
$this->insert($number);
}
}
public function insert($number)
{
$this->numbers[] = $number;
if ($this->lowerHeap->count() == 0 || $number < $this->lowerHeap->top()) {
$this->lowerHeap->insert($number);
} else {
$this->higherHeap->insert($number);
}
$this->balance();
}
protected function balance()
{
$biggerHeap = $this->lowerHeap->count() > $this->higherHeap->count() ? $this->lowerHeap : $this->higherHeap;
$smallerHeap = $this->lowerHeap->count() > $this->higherHeap->count() ? $this->higherHeap : $this->lowerHeap;
if ($biggerHeap->count() - $smallerHeap->count() >= 2) {
$smallerHeap->insert($biggerHeap->extract());
}
}
public function getMedian()
{
if (!count($this->numbers)) {
return null;
}
$biggerHeap = $this->lowerHeap->count() > $this->higherHeap->count() ? $this->lowerHeap : $this->higherHeap;
$smallerHeap = $this->lowerHeap->count() > $this->higherHeap->count() ? $this->higherHeap : $this->lowerHeap;
if ($biggerHeap->count() == $smallerHeap->count()) {
return ($biggerHeap->top() + $smallerHeap->top())/2;
} else {
return $biggerHeap->top();
}
}
}
And then the code to benchmark:
$array = [];
for($i=0; $i<100000; $i++) {
$array[] = mt_rand(1,100000) / mt_rand(1,10000);
}
$t = microtime(true);
echo array_median($array);
echo PHP_EOL . 'Sort Median: ' . (microtime(true) - $t) . ' seconds';
echo PHP_EOL;
$t = microtime(true);
$list = new MedianHeap($array);
echo $list->getMedian();
echo PHP_EOL . 'Heap Median: '. (microtime(true) - $t) . ' seconds';
Is there something in PHP that makes using heaps for this inefficient somehow or is there something wrong with my implemenation?

PHP URL Shortener error

I have this PHP code which is supposed to increase a URL shortener mask on each new entry.
My problem is that it dosen't append a new char when it hits the last one (z).
(I know incrementing is a safety issue since you can guess earlier entries, but this is not a problem in this instance)
If i add 00, it can figure out 01 and so on... but is there a simple fix to why it won't do it on its own?
(The param is the last entry)
<?php
class shortener
{
public function ShortURL($str = null)
{
if (!is_null($str))
{
for($i = (strlen($str) - 1);$i >= 0;$i--)
{
if($str[$i] != 'Z')
{
$str[$i] = $this->_increase($str[$i]);
#var_dump($str[$i]);
break;
}
else
{
$str[$i] = '0';
if($i == 0)
{
$str = '0'.$str;
}
}
}
return $str;
}
else {
return '0';
}
}
private function _increase($letter)
{
//Lowercase: 97 - 122
//Uppercase: 65 - 90
// 0 - 9 : 48 - 57
$ord = ord($letter);
if($ord == 122)
{
$ord = 65;
}
elseif ($ord == 57)
{
$ord = 97;
}
else
{
$ord++;
}
return chr($ord);
}
}
?>
Effectively, all you are doing is encoding a number into Base62. So if we take the string, decode it into base 10, increment it, and reencode it into Base62, it will be much easier to know what we are doing, and the length of the string will take care of itself.
class shortener
{
public function ShortURL($str = null)
{
if ($str==null) return 0;
$int_val = $this->toBase10($str);
$int_val++;
return $this->toBase62($int_val);
}
public function toBase62($num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$r = $num % $b ;
$res = $base[$r];
$q = floor($num/$b);
while ($q) {
$r = $q % $b;
$q =floor($q/$b);
$res = $base[$r].$res;
}
return $res;
}
function toBase10( $num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$limit = strlen($num);
$res=strpos($base,$num[0]);
for($i=1;$i<$limit;$i++) {
$res = $b * $res + strpos($base,$num[$i]);
}
return $res;
}
}

Recursive function loops infinitely [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am working on a personal project, a bot that plays connect 4. So something is seriously wrong with the recursive function I have written to manage the bots move. No errors are thrown, and any debug info I can be shown does not tell me anything useful. Additionally, I am sure that I have not overflown my stack and that my php.ini file is good to go. This script just runs (consumes a sane amount of memory) and never returns. Only about 2400 function calls should be happening, so this script should return after only a second or two. This has stumped me for days. I believe there is something I have not properly researched. Additonally I should mention that the game board is a simple 2D array to simulate a 7 x 7 board. ai_move_helper is the recursive function and I just cannot figure out why it will not function correctly.
// this class is a code igniter library
class ConnectFour
{
public function __construct()
{
// these are expensive opperations this will give the server enough time
set_time_limit(0);
ini_set('memory_limit', '2048M');
}
public $board_width = 7;
public $board_length = 7;
public $game_array = array();
public $depth_limit = 3;
// this function gets a human made move from a CI controller ($game board)
// and returns the board after the new AI move is applied
public function ai_player_move($game_array, $active_players_move)
{
$this->game_array = $game_array;
$this->game_array = $this->calculate_ai_move($active_players_move);
return $this->game_array;
}
public function calculate_ai_move($active_players_move)
{
$move_weight_array = array();
$prime_game_board = array();
// we hardcast the active player because at this point we know it is the AI
// here we also have to prime the move computer
for($q = 0; $q < $this->board_length; $q++)
{
// MAGIC NUMBERS are the active players!!!
$prime_game_board[] = $this->apply_prime_move($q, 2, $this->game_array);
$move_weight_array[] = $this->ai_move_helper($prime_game_board[$q], 2, 0);
}
//choose your move
for($u = 0; $u < $this->board_length; $u)
{
if($move_weight_array[$u][0] == 1)
{
return $prime_game_board[$u];
}
}
// otherwise return a random acceptable move
$random = rand(0, 6);
return $prime_game_board[$random];
}
public function ai_move_helper($game_board, $active_player, $depth)
{
// build the object that will be needed at this level of recusion
$depth = $depth + 1;
$score_object = new stdClass;
$move_array = array();
$game_boards_generated_at_this_level = array();
$new_game_boards_generated_at_this_level = array();
$return_end_state_detected = 0;
$score_agregate = array();
if($this->depth_limit < $depth)
{
$score_agregate[0] = 0;
$score_agregate[1] = 0;
return $score_agregate;
}
$active_player = ($active_player == 1) ? 2 : 1;
// check for possible moves
for($i=0; $i < $this->board_width; $i++)
{
// calculate all of the possible recusions (all of the next moves)
$game_boards_generated_at_this_level[$i] = $this->apply_ai_move($i, $active_player, $game_board);
// this is the recusive level
$score_agregate = $this->ai_move_helper($game_boards_generated_at_this_level[$i]->game_board, $active_player, $depth);
}
// check to see if there are more moves of if it is time to return
foreach($game_boards_generated_at_this_level as $game_state)
{
//compute the agragate of the scores only for player two (AI)
if($active_player == 2)
{
$score_agregate[0] = $score_agregate[0] + $game_state->score_array[0];
$score_agregate[1] = $score_agregate[1] + $game_state->score_array[1];
}
}
return $score_agregate;
}
public function apply_ai_move($move, $active_players_move, $board_to_use)
{
$board_for_function = array();
$location_of_new_pieces = 0;
$return_object = new stdClass;
// this makes sure that this function is being called with the right board
if(!empty($board_to_use))
{
$board_for_function = $board_to_use;
} else {
$board_for_function = $this->game_array;
}
// check that this move is possible
if(!$this->move_possible($move, $board_for_function))
{
$return_object->game_board = NULL;
$return_object->score_array = NULL;
return $return_object;
}
// this part of the function applies a valid move
foreach($board_for_function[$move] as $column_key => $column_space)
{
// check if you are at the edge of an empty row
if(!array_key_exists(($location_of_new_pieces + 1), $board_for_function[$move]) && $column_space == '_')
{
$board_for_function[$move][$location_of_new_pieces] = ($active_players_move == 1) ? 'x' : 'o';
break;
}
// check if the next place has stuff in it too
if($column_space != '_')
{
// check the edge of the board to make sure that exists
if(array_key_exists(($location_of_new_pieces - 1), $board_for_function))
{
$board_for_function[$move][$location_of_new_pieces - 1] = ($active_players_move == 1) ? 'x' : 'o';
break;
} else {
echo "well fuck...1"; exit;
}
}
$location_of_new_pieces++;
}
$return_object->game_board = $board_for_function;
// now check if this state is a win loss or draw
$test_for_complete = $this->check_for_winner_or_draw($board_for_function, $active_players_move);
// this is a draw
if($test_for_complete == -1)
{
$return_object->score_array = array(0, 1);
} else if($test_for_complete > 3) {
$return_object->score_array = array(1, 0);
} else {
$return_object->score_array = array(0, 0);
}
return $return_object;
}
public function apply_prime_move($move, $active_players_move, $board_to_use)
{
$location_of_new_pieces = 0;
foreach($board_to_use[$move] as $column_key => $column_space)
{
// check if you are at the edge of an empty row
if(!array_key_exists(($location_of_new_pieces + 1), $board_to_use[$move]) && $column_space == '_')
{
$board_to_use[$move][$location_of_new_pieces] = ($active_players_move == 1) ? 'x' : 'o';
break;
}
// check if the next place has stuff in it too
if($column_space != '_')
{
// check the edge of the board to make sure that exists
if(array_key_exists(($location_of_new_pieces - 1), $board_to_use))
{
$board_to_use[$move][$location_of_new_pieces - 1] = ($active_players_move == 1) ? 'x' : 'o';
break;
} else {
echo "well fuck...1"; exit;
}
}
$location_of_new_pieces++;
}
return $board_to_use;
}
public function move_possible($move, $game_board)
{
// check that this move is not going to fall out of the board
if($game_board[$move][0] != "_")
{
return FALSE;
} else {
return TRUE;
}
}
public function check_for_winner_or_draw($game_array, $active_player_move)
{
$present_possible_winner = "";
$count_to_win = 0;
$game_not_a_draw = FALSE;
for($i = 0; $i < $this->board_length; $i++)
{
for($j = 0; $j < $this->board_width; $j++)
{
// start checking for a winner
if($game_array[$i][$j] != "_")
{
$present_possible_winner = $game_array[$i][$j];
// check for a winner horizontally
for($x = 0; $x < 4; $x++)
{
if($j+$x < $this->board_width)
{
if($game_array[$i][$j+$x] == $present_possible_winner)
{
$count_to_win = $count_to_win + 1;
}
}
}
if($count_to_win > 3)
{
return $present_possible_winner; // this player has won
} else {
$count_to_win = 0;
}
// check for a winner horizontally
for($y = 0; $y < 4; $y++)
{
if($i+$y < $this->board_width)
{
if($game_array[$i+$y][$j] == $present_possible_winner)
{
$count_to_win = $count_to_win + 1;
}
}
}
if($count_to_win > 3)
{
return $present_possible_winner; // this player has won
} else {
$count_to_win = 0;
}
// check for a winner up to down diagonal
for($z = 0; $z < 4; $z++)
{
if(($i+$z < $this->board_width) && ($j+$z < $this->board_length))
{
if($game_array[$i+$z][$j+$z] == $present_possible_winner)
{
$count_to_win = $count_to_win + 1;
}
}
}
if($count_to_win > 3)
{
return $present_possible_winner; // this player has won
} else {
$count_to_win = 0;
}
// check for a winner down to up diagonal
for($w = 0; $w < 4; $w++)
{
if(($i+$w < $this->board_width) && ($j-$w >= 0))
{
if($game_array[$i+$w][$j-$w] == $present_possible_winner)
{
$count_to_win = $count_to_win + 1;
}
}
}
if($count_to_win > 3)
{
return $present_possible_winner; // this player has won
} else {
$count_to_win = 0;
}
}
}
}
// check for a drawed game and return accordingly
for($i = 0; $i < $this->board_length; $i++)
{
for($j = 0; $j < $this->board_width; $j++)
{
if($game_array[$i][$j] == "_")
{
$game_not_a_draw = TRUE;
}
}
}
if(!$game_not_a_draw)
{
return -1;
}
return 0;
}
// this is a private debugging function that I wrote for this script
public function debug($value = NULL, $name = NULL, $exit = NULL)
{
if(!empty($name))
{
echo $name . "<br />";
}
echo "<pre>";
var_dump($value);
echo "</pre>";
if($exit)
{
exit;
}
}
}
Well I found it: The calculate ai move had an infinite loop in it...
//choose your move
for($u = 0; $u < $this->board_length; $u)
{
if($move_weight_array[$u][0] == 1)
{
return $prime_game_board[$u];
}
}
Should be
//choose your move
for($u = 0; $u < $this->board_length; $u++)
{
if($move_weight_array[$u][0] == 1)
{
return $prime_game_board[$u];
}
}
Back to work for me...

auto increasing id

I would like to build a php script that automatically generates a new id by increasing the previous by 1.
eg: A0009 becomes A0010 and A9999 becomes B0000
I have written one that works but it doesn't go over 5 chars long:
eg: Z9999 should go to A00000 and so on.
Any suggestions?
here is my snippet:
<?php
function replaceChar($string2replace)
{
$charLength = strlen($string2replace)-1;
$charAt = array();
$charAt[4] = substr($string2replace, -1);
$charAt[3] = substr($string2replace, -2,1);
$charAt[2] = substr($string2replace, -3,1);
$charAt[1] = substr($string2replace, -4,1);
$charAt[0] = substr($string2replace, 0,1);
if($charAt[4] < 9)
{
$string2replace = substr_replace($string2replace,$charAt[4]+1,$charLength);
}
else
{
$charAt[4] = 0;
$string2replace = substr_replace($string2replace,$charAt[4],$charLength);
if($charAt[3] < 9)
{
$string2replace = substr_replace($string2replace,$charAt[3]+1,$charLength- 1,1);
}
else
{
$charAt[3] = 0;
$string2replace = substr_replace($string2replace,$charAt[3],$charLength-1,1);
if($charAt[2] < 9)
{
$string2replace = substr_replace($string2replace,$charAt[2]+1,$charLength-2,1);
}
else
{
$charAt[2] = 0;
$string2replace = substr_replace($string2replace,$charAt[2],$charLength-2,1);
if($charAt[1] < 9)
{
$string2replace = substr_replace($string2replace,$charAt[1]+1,$charLength-3,1);
}
else
{
$charAt[1] = 0;
$string2replace = substr_replace($string2replace,$charAt[1],$charLength-3,1);
}
if($charAt[0] < 'z')
{
$charAt[0] ++;
$string2replace = substr_replace($string2replace,$charAt[0],$charLength-4,1);
}
else
{
$charAt[0] = 'a';
$string2replace = substr_replace($string2replace,$charAt[0],$charLength-4,1);
}
}
}
}
return $string2replace;
}
$string2begin = 'A9999';
$generatedString = replaceChar($string2begin);
echo $string2begin . "<br />" . $generatedString;
?>
Your ID numbering scheme seems rather contrived, where the high-order digit is A-Z and the remaining digits are 0-9. If I understand that pattern correctly, this seems to do the trick:
function incrementID($id)
{
$letter = $id[0];
$number = substr($id, 1);
$newNum = str_pad($number + 1, strlen($number), '0', STR_PAD_LEFT);
// increase number only
if (strlen($number) == strlen($newNum))
return $letter . $newNum;
// increase ID length ('Z' to 'A')
if ($letter == 'Z')
return 'A' . str_repeat('0', strlen($number) + 1);
// change letter
$newLetter = chr(ord($letter) + 1);
return $newLetter . str_repeat('0', strlen($number));
}
printf("%s\n", incrementID('A0009')); // 'A0010'
printf("%s\n", incrementID('A9999')); // 'B0000'
printf("%s\n", incrementID('Z9999')); // 'A00000'
Even though your examples didn't fit this, I first assumed you really just wanted a base-36 number (any digit could be 0-9,A-Z, where A is 10 and Z is 35). Working with numbers in base-36 is easy because you can use base_convert() to convert them to customary base-10. This is all you would need to do to increment base-36 numbers:
function incrementBase36($id)
{
$numVal = base_convert($id, 36, 10);
$newId = base_convert($numVal + 1, 10, 36);
return strtoupper($newId);
}
printf("%s\n", incrementBase36('A0009')); // 'A000A'
printf("%s\n", incrementBase36('A9999')); // 'A999A'
printf("%s\n", incrementBase36('Z9999')); // 'Z999A'
printf("%s\n", incrementBase36('AZZZZ')); // 'B0000'
printf("%s\n", incrementBase36('ZZZZZ')); // '100000'

Categories