Неправильная работа функции array_rand

WBS

Новичок
Неправильная работа функции array_rand

Создаем массив из $i_max элементов. Делаем 10 случайных выборок по 20 элементов в каждой.

Код:

PHP:
<?

$i_max = 30000;
for($i=0; $i<$i_max; $i++)
  $arr[] = $i;

for ($i=0; $i<10; $i++) {
  $rand_ids = array_rand($arr, 20);
  for ($i2=0; $i2<20; $i2++)
    print($rand_ids[$i2]."<br>");

  print("<p>");
}

?>
Получаем:
PHP:
.
  869	 1974	 1673	  583	  524	 1591	  237	 5118	  869	 1974
 2078	 2886	 2049	  875	 5299	 3832	 1155	 5709	 2078	 2886
 2432	 4044	 2149	 5094	 5591	 5166	 2393	 6627	 2432	 4044
 3404	 4914	 3140	 5470	 9810	 5691	 2990	 7865	 3404	 4914
 8523	 6123	 5115	 5570	10186	10466	 3400	 8462	 8523	 6123
 9114	 6477	 6027	 6561	10286	10758	 4992	 8872	 9114	 6477
10032	 7449	 7185	 8536	11277	14977	 7233	10464	10032	 7449
11270	12568	 9264	 9448	13252	15353	 8567	12705	11270	12568
11867	13159	 9618	10606	14164	15453	 9092	14039	11867	13159
12277	14077	10590	12685	15322	16444	13867	14564	12277	14077
13869	15315	16300	13039	16192	18419	14159	17236	13869	15315
16110	15912	17218	14011	17401	19331	18378	19339	16110	15912
17444	16322	18456	19130	17755	20489	18754	19631	17444	16322
17969	17914	19053	19721	18727	21359	18854	23850	17969	17914
22744	20155	19463	20639	23846	22568	19845	24226	22744	20155
23036	21489	21055	21877	24437	22922	21820	24326	23036	21489
27255	22014	23296	22474	25355	23894	22732	25317	27255	22014
27631	26789	24630	22884	26593	29013	23890	27292	27631	26789
27731	27081	25155	26717	27190	29221	26323	28204	27731	27081
28722	29626	29346	28051	27600	29366	27295	29362	28722	29626
Выборки 1 и 9, а также 2 и 10 получились одинаковыми. Почему?

При увеличении $i_max (размера массива) последовательности начинают повторяться чаще. Т.е. из массива начинают извлекаться не случайные элементы, а одни и те же.
Тесты проводились на ОС Windows Vista SP2, PHP 5.3.0.
 

dimagolov

Новичок
Как тебе удалось так отформатировать вывод? он же в один столбик идет.

Твой пример у меня не то что идентичных выборок не давал, но и первые элементы каждой выборки были уникальны среди всех выбранных элементов. WinXP PHP 5.2.6

-~{}~ 17.08.09 11:25:

хотя интересно, что при 2-м запросе 10-я выбрпка совпала с твоей 8-й, а на 3-й полностью с твоим набором, на 4-й тоже было много совпадений с твоей выборкой
 

Crys

Двинутый новичок
Осмелюсь предположить, что тут тоже самое, что и с rand

-~{}~ 17.08.09 17:44:

Твой пример у меня не то что идентичных выборок не давал, но и первые элементы каждой выборки были уникальны среди всех выбранных элементов. WinXP PHP 5.2.6
Выдает, выдает...

Код:
<?

$i_max = 30000;
for($i=0; $i<$i_max; $i++)
  $arr[] = $i;

$res = array();
  
print '<table><tr>';

for ($i=0; $i<30; $i++) {
print '<td>';
  $rand_ids = array_rand($arr, 20);
  $str = '';
  for ($i2=0; $i2<20; $i2++) {
    print($rand_ids[$i2]."<br>");
	$str.=$rand_ids[$i2];
  }
$res[] = $str;
print '</td>';
}
print '</tr></table>';

print 'Total: '.count($res).'<br>';
print 'Uniq: '.count(array_unique($res));

?>
Сразу выдало
Total: 30
Uniq: 17

Потом раз двадцать
Total: 30
Uniq: 8

-~{}~ 17.08.09 17:46:

Под линуксом глянул - все в порядке.. Повторов нет.
 

WBS

Новичок
Автор оригинала: dimagolov
Как тебе удалось так отформатировать вывод? он же в один столбик идет.
Я сделал это вручную перед размещением на форум :).

Автор оригинала: Crys
Под линуксом глянул - все в порядке.. Повторов нет.
У меня было подозрение, что проблема будет только в Win. Я под Линуксом не проверял, т.к. мне нужен рабочий скрипт именно в Win. До сих пор ищу хорошее решение...

Автор оригинала: Beavis
а если делать маленькие паузы перед каждым array_rand ?
Не поможет. Тем более одинаковые результаты можно получить даже делая разные независимые запуски скрипта.

Автор оригинала: Crys
А вот srand(); спасает..
К сожалению, не спасает. Оно и не мудрено, ведь "Начиная с PHP 4.2.0, больше нет необходимости инициализировать генератор случайных чисел функциями srand() или mt_srand(), поскольку теперь это происходит автоматически."


Если один случайный элемент массива еще можно выбрать, используя mt_rand(), то с выборкой нескольких случайных элементов без повторов возникают некоторые сложности и проблемы. Хорошая работающая функция вроде array_rand() была бы очень кстати :).

Если это действительно баг, а не моя ошибка, то было бы неплохо, чтобы кто-нибудь (кто хорошо пишет по англ.) сообщил о нем в
http://bugs.php.net/
 

baev

‹°°¬•
Команда форума
WBS, попробуйте после каждого "array_rand($arr, 20)" вставить:
PHP:
reset ($arr);
 

tf

крылья рулят
Если это действительно баг, а не моя ошибка, то было бы неплохо, чтобы кто-нибудь (кто хорошо пишет по англ.) сообщил о нем в
http://bugs.php.net/
помоему они там тебя пашлют, это какой-то задокументированный баг)
удачи в поисках
 

DiMA

php.spb.ru
Команда форума
уши вянут от детского сада

1. чтобы накатать багрепорт - никакой английский или многодневные дебаты не нужны, достаточно наглядного примера и пары фраз со словарем

2. напиши автоматизированный тест, выявляющий повторы рандомных рядов, чтобы было о чем говорить и проводить эксперименты по самостоятельной борьбе с багами (полдня профукали - ничего по проблеме не сделано)
 

Crys

Двинутый новичок
К сожалению, не спасает. Оно и не мудрено, ведь "Начиная с PHP 4.2.0, больше нет необходимости инициализировать генератор случайных чисел функциями srand() или mt_srand(), поскольку теперь это происходит автоматически."
Да-да-да.. я это тоже слышал.. Но у меня PHP 5.2.4 и факт имеет место быть:
1) Без srand (генерируем 30 массивов)
Total: 30
Uniq: 8
2) С srand (генерируем 30 массивов)
Total: 30
Uniq: 30

но не все так прекрасно, т.к:

3) С srand (300 массивов)
Total: 300
Uniq: 299

4) C srand (3000 массивов)
Total: 3000
Uniq: 2882
5) Без srand (3000 массивов)
Total: 3000
Uniq: 12

6) C mt_srand (вот он тут нафик не нужен)
Total: 3000
Uniq: 10

А говорите, srand ничего не делает...
 

Crys

Двинутый новичок
PHP 5.3.0
Без srand (3000 массивов)
Total: 3000
Uniq: 3000

О как..

-~{}~ 18.08.09 13:58:

На никсах будут другие цифры.
Может ты сначала прочитаешь предыдущие реплики?
 

WBS

Новичок
Автор оригинала: Crys
А говорите, srand ничего не делает...
Лично мне srand не помогает. Я пробовал использовать srand еще до размещения темы на форуме :).


Пришлось мне написать собственную функцию. Выкладываю код, может кому пригодится...

PHP:
<?

function array_rand_win ($array, $num_req=1) {
  $size = sizeof($array);
  if ($num_req>$size)
    return FALSE;

  $indexes = array();
  for ($cur_index=0; $cur_index<$num_req; $cur_index++) {
    $next_rand = mt_rand(0, $size-$cur_index-1);
    foreach($indexes as $ind) {
      if ($ind<=$next_rand)
        $next_rand++;
      else
        break;
    }
    $indexes[] = $next_rand;
    sort($indexes);
  }

  $keys = array_keys($array);

  if ($num_req==1)
    return $keys[$indexes[0]];

  $return_array = array();
  foreach($indexes as $ind)
    $return_array[] = $keys[$ind];
  return $return_array;
}

// ------------------------------

$i_max = 100000;
for ($i=0; $i<$i_max; $i++)
  $arr[] = $i;

print("<table><tr>");
for ($i=0; $i<10; $i++) {
  $rand_ids = array_rand_win($arr, 30);
  print("<td style=\"padding: 0 20px\">");
  foreach($rand_ids as $rid)
    print($rid."<br>");
  print("</td>");
}
print("</tr></table>");

?>
Функция array_rand_win() - это полный аналог array_rand(). Как и положено, работает в т.ч. и с ассоциативными массивами. Работает несколько медленнее array_rand, но зато, вроде, правильно.
 

tf

крылья рулят
5.3 теже цифры
Работает несколько медленнее array_rand, но зато, вроде, правильно.
может лучше указать требования к хостеру для продукта?
если оно того стоит

-~{}~ 18.08.09 19:05:

накопал небольшой бред
изменение сида
Использование md5 хэша в качестве сида. Такой способ наиболее часто используется в качественных веб-приложениях: имеется собственная функция для генерации случайных чисел, которая при каждом вызове извлекает md5 хэш из базы данных (или другого источника), модифицирует его, назначает в качестве сида и генерирует случайное число, исходя из этого md5 значения.
PHP:
function random() {
	static $rand_seed = 'efefegf';

	$val = $rand_seed . microtime();
	$val = md5($val);
	$rand_seed = md5($rand_seed . $val);

	srand(crc32($val));
}
for ($i=0; $i<30; $i++) {
  random();
по выкладкам, аналогичен твоей функции, отсутствует тормазнутость
 
Сверху