/* PHP & MySQL Journal */
The last programming problem, though not difficult, was interesting for the variety of ways readers tried to solve it. Here goes the second one:
Using a standard deck of playing cards we can display the first n digits of the constant PI. For example using a single deck of cards, you can display the following digits of PI:
3.14159265358979323846264
In the above example we cannot display the next digit, which happens to be a ‘3′, because we have already used 4 cards of the number ‘3′ from a single deck.
Write a function that takes d - the number of card decks; and returns the number of digits of PI you can display using the cards. So for d(deck) = 1, the function should return 24 (the number of starting digits of PI).
Take ‘Ace’ as the digit ‘1′ and ‘10′ as the digit ‘0′. A list of the million digits of PI can be found here.
Preferably the solution should be in PHP, but other languages are welcome.
UPDATE: 18th Dec. 2009
The following are 2 solutions provided by readers.
By Daniel Egeberg:
Need to first copy the primes from here to a text file.
<?php function getDeck($n) { if (!is_int($n) || $n < 1) { throw new DomainException('Number of decks must be positive integer.'); } return array_combine( range(0, 10), array_fill(0, 11, 4 * $n) ); } function numDigits($decks, $piFile, $bufferSize = 20) { $deck = getDeck($decks); if (!file_exists($piFile)) { throw new InvalidArgumentException('Pi file does not exist.'); } if (!is_int($bufferSize) || $bufferSize < 1) { throw new DomainException('Buffer size must be positive integer.'); } $fp = fopen('pi.txt', 'r'); $count = 0; while (!feof($fp)) { $buffer = fread($fp, $bufferSize); for ($i = 0, $s = strlen($buffer); $i < $s; ++$i) { $d = $buffer[$i]; if (!preg_match('#[0-9]#s', $d)) { continue; } if ($deck[$d] === 0) { break 2; } ++$count; --$deck[$d]; } } fclose($fp); $foundZero = false; foreach ($deck as $n) { if ($n === 0) { $foundZero = true; } } if (!$foundZero) { throw new RuntimeException('Ran out of digits.'); } return $count; } try { if ($argc !== 3) { throw new InvalidArgumentException(sprintf('Usage: %s numDecks piFile', $argv[0])); } $decks = (int) $argv[1]; $piFile = $argv[2]; printf('Using %d deck(s) you can get %d digits of pi' . PHP_EOL, $decks, numDigits($decks, $piFile)); } catch (Exception $e) { fprintf(STDERR, $e->getMessage() . PHP_EOL); exit(1); } ?> |
by Marcin
/** * @author marcin@php4u.co.uk */ puzzle2solver::solve(1); class puzzle2solver { /** * Gets first $precision numbers of pi * @param int $precision */ public static function getPiNumbers($precision) { echo "Getting PI..."; $pi_numbers = substr(preg_replace('/[^0-9]/','', strip_tags(file_get_contents('http://www.exploratorium.edu/pi/Pi10-6.html', FILE_TEXT))),0,$precision); echo "Ok\n"; return $pi_numbers; } /** * Solver * * @param int $decks */ public static function solve($decks) { if (is_int($decks) && $decks > 0) { $length = $decks * 40; // this is maximum what we can get $pi_numbers = self::getPiNumbers($length); // take our pi numbers $starttime = microtime(true); $deck = new deck($decks); // initiate deck object $n=0; while ($n<$length) { if (!$deck->takeCard($pi_numbers[$n])) { // take our number from the stock break; // if failed we don't have more cards } $result .= $pi_numbers[$n]; $n++; // take next number } $time = microtime(true)-$starttime; echo "Solution: {$result} [{$n} numbers]\n"; echo "Time: ".sprintf("%2.10f",$time)." secs\n"; } else { echo "usage: php ".__file__." number_of_decks\n"; } } } /** * Class which represents our decks */ class deck { /** * How many decks we have * * @param int $decks_number */ public function __construct($decks_number) { $this->decks_number = $decks_number * 4; // 4 cards from each deck $this->decks = array_fill(0, 10, 0); } /** * Takes card (we increase our card counter), returns false if we ran out of cards * * @param int $number [0-9] * @return boolean */ public function takeCard($number) { if ($this->decks[$number]==$this->decks_number) { return false; } else { $this->decks[$number]++; return true; } } } |
|
|
This site is a digital habitat of Sameer, a freelance web developer working from Pune.More
5 Responses
1
Daniel Egeberg
November 8th, 2009 at 12:38 pm
When the digits of pi are pre-calculated, doing that is fairly trivial by just keep reading from a file containing them.
<?phpfunction getDeck($n)
{
if (!is_int($n) || $n < 1) {
throw new DomainException('Number of decks must be positive integer.');
}
return array_combine(
range(0, 10),
array_fill(0, 11, 4 * $n)
);
}
function numDigits($decks, $piFile, $bufferSize = 20)
{
$deck = getDeck($decks);
if (!file_exists($piFile)) {
throw new InvalidArgumentException('Pi file does not exist.');
}
if (!is_int($bufferSize) || $bufferSize < 1) {
throw new DomainException('Buffer size must be positive integer.');
}
$fp = fopen('pi.txt', 'r');
$count = 0;
while (!feof($fp)) {
$buffer = fread($fp, $bufferSize);
for ($i = 0, $s = strlen($buffer); $i getMessage() . PHP_EOL);
exit(1);
}
That works reasonably fast. If you were ensured a file that was always correctly filled you could skip the regex check. Increasing the buffer size would also make it faster and reading the entire file would be even faster because of minimized I/O. It would consume more memory, however.
2
Daniel Egeberg
November 8th, 2009 at 12:39 pm
Hmm… the commenting system seems to have screwed up my code. I’ve pasted it here:
http://pastie.org/689167
3
Marcin
November 9th, 2009 at 3:24 pm
My entry:
http://pastie.org/691008
Getting PI number from url is pretty slow, rest is quick.
Can’t wait for other “smart” more math solutions
4
Gabriel Duman
December 16th, 2009 at 7:37 am
here is a shorter solution. i’ve copied the regex of marcin #3. sorry for that.
<?
/**
* @param integer $d number of decks
* @param string $pi Pi
* @return integer
*/
function getLengthOfPi($d, $pi) {
$counter = array();
$i=0;
do {
$counter[$pi[$i]]++;
$i++;
} while ($counter[$pi[$i]]
5
Gabriel Duman
December 16th, 2009 at 7:38 am
code-tag did not work. here again:
http://pastie.org/745653