У меня есть ассоциативный массив, т.е.
$primes = array(
2=>2,
3=>3,
5=>5,
7=>7,
11=>11,
13=>13,
17=>17,
// ...etc
);
тогда я делаю
// seek to first prime greater than 10000
reset($primes);
while(next($primes) < 10000) {}
prev($primes);
// iterate until target found
while($p = next($primes)) {
$res = doSomeCalculationsOn($p);
if ( IsPrime($res) )
return $p;
}
Проблема в том, что IsPrime также перебирает массив $ primes,
function IsPrime($num) {
global $primesto, $primes, $lastprime;
if ($primesto >= $num)
// using the assoc array lets me do this as a lookup
return isset($primes[$num]);
$root = (int) sqrt($num);
if ($primesto < $root)
CalcPrimesTo($root);
foreach($primes as $p) { // <- Danger, Will Robinson!
if ( $num % $p == 0 )
return false;
if ($p >= $root)
break;
}
return true;
}
который уничтожает указатель на массив, который я повторяю.
Я хотел бы иметь возможность сохранять и восстанавливать внутренний указатель массива в функции IsPrime (), чтобы он не имел этого побочного эффекта. Есть какой-либо способ сделать это?






Вы можете «сохранить» состояние массива:
$state = key($array);
И «восстановить» (не уверен, есть ли способ лучше):
reset($array);
while(key($array) != $state)
next($array);
Эм ... правильный эффект, но это O (n) в размере массива, что делает мой алгоритм O (n ^ 2) неприемлемым. Интересно, насколько сложно будет продлить сброс ($ arr, $ key = 0)? (Сбросить до начала или до указанной пары "ключ-значение"?)
Этот ответ может быть не лучшим, но это правильный ответ на вопрос ... imo.
Если скорость не является проблемой, и вы не нажимаете на ограничения памяти php, самым быстрым решением будет просто продублировать ваш массив простых чисел и повторить 2 разных.
$awesomePrimes=$primes;
Затем измените глобальные переменные и foreach в своей функции на $awesomePrimes.
Для массива, содержащего 2 миллиона элементов, который использует дополнительные 68 МБ памяти - не конец света, но кажется расточительным.
Не полагайтесь на указатели на массивы. Вместо этого используйте итераторы.
Вы можете заменить свой внешний код на:
foreach ($primes as $p) {
if ($p > 10000 && IsPrime(doSomeCalculationsOn($p))) {
return $p;
}
}
(хлопая по лбу) Хорошо, это работает - я думал, что foreach () использует собственный указатель массива, но, видимо, нет. делает сбрасывает внутренний указатель массива, что является нечетным побочным эффектом?
Я не уверен, как работает внутренняя реализация foreach, но она поддерживает собственное состояние. Я не помню, чтобы когда-либо использовал указатели на массивы для просмотра списков в PHP.
Кстати. Как-то странно использовать ассоциативный массив для ваших значений. Я бы использовал простой список, а затем использовал бы in_array (), чтобы проверить, существует ли значение.
Как насчет создания еще одного массива int -> int, где индекс - это порядковый номер от 0 до п, а значение - это индекс ассоциативного массива? Итак, у вас будет:
$pointer = array(
0 => 2,
1 => 3,
2 => 5,
// ...
);
и вместо прямой ссылки на $prime вы бы использовали $prime[$pointer[$i]] или что-то подобное?
используйте цикл «for» для одной из ваших итераций. например, используйте этот цикл в своем методе IsPrime:
$primesLength = count($primes); // this is to avoid calling of count() so many times.
for ($counter=0 ; $counter < $primesLength ; $counter++) {
$p = $primesLength[$counter];
if ( $num % $p == 0 )
return false;
if ($p >= $root)
break;
}
таким образом, указатель внутреннего массива не будет использоваться в методе.
+1 за опасность, Уилл Робинсон!