Почему Не работает rand() при открытии большого файла по file()

shift_on_line

Новичок
Здравствуйте, подскажите пожалуйста.

PHP:
$file_keyword = file("key/bigfile.txt");
echo $file_keyword[array_rand($file_keyword)].'<hr>';
echo $file_keyword[array_rand($file_keyword)].'<hr>';
echo $file_keyword[array_rand($file_keyword)].'<hr>';
echo $file_keyword[array_rand($file_keyword)].'<hr>';
echo rand().'<hr>';
echo rand().'<hr>';
echo rand().'<hr>';
echo rand().'<hr>';
запускаю на локальном компе, проверял на денвере и опенсервер
если открывать большой файл (на 33к строк и 1мб веса) то функция ранд всегда выдаёт всего 2 уникальных значения и всего 2 записи из файла.

обновляешь и одно и тоже.

если файл маленький, то всё нормально, числа случайны

проверил на хостинге в интернете - там всё отлично на большом файле.

подскажите как настроить локальный сервер чтоб функция ранд нормально работала, я думаю чтото в настройках памяти..

пс. у меня w7 64, хар-ки достойные, т.е. дело не в железе.
 

radioheaded

PHP нуб
подскажите как настроить локальный сервер чтоб функция ранд нормально работала, я думаю чтото в настройках памяти..
Настроить его под линуксом.
Если сходить сюда — http://php.net/manual/ru/function.rand.php — и почитать, то можно найти замечание относительно Windows. Оно не напрямую к вам относится, но как бы намекает. Если у вас удаленный сервер на линукс, то глупо иметь локальный на другой оси. Влом ставить отдельно линукс — поставьте виртуалку, у вас же характеристики достойные, все будет хорошо.
 

shift_on_line

Новичок
Настроить его под линуксом.
Если сходить сюда — http://php.net/manual/ru/function.rand.php — и почитать, то можно найти замечание относительно Windows. Оно не напрямую к вам относится, но как бы намекает. Если у вас удаленный сервер на линукс, то глупо иметь локальный на другой оси. Влом ставить отдельно линукс — поставьте виртуалку, у вас же характеристики достойные, все будет хорошо.
ставить линукс для локального написания скриптов из за неработающей функции Имхо не стоит

она тоже неработает, и как я понял из за переполнения памяти из за большого файла.

если открывать файл fopen то можно взять случайную..
 

shift_on_line

Новичок
сделал проще

PHP:
function keyword_rand($file_keyword) {
  $f = fopen($file_keyword, 'r'); 
  $i = 0; 
  while(!feof($f)) { 
    $tmp = fgets($f, 8196); 
    if (!mt_rand(0, $i++)) $string = $tmp; 
  } 
  fclose($f);
  return trim($string);
}
всё работает и память не жрёт.
 

hell0w0rd

Продвинутый новичок
А вообще не пугает, что 1 число всегда будет 0, второе 0 или 1, и так далее, смысл таким псевдо-пипец-как-псевдо случайные числа проставлять?)
PHP:
$arr = array();
$stat = array();
$max = 1000;
for($i = 0; $i<$max; $i++){
    $stat[$i] = 0;
}
for($j = 10000; $j--;){
    for($i = 0; $i<$max; $i++){
        $arr[$i] = mt_rand(0, $i);
    }
    if(isset($prev)){
        for($i = 0; $i<$max; $i++){
            if($arr[$i] == $prev[$i]){
                $stat[$i]++;
            }
        }
    }
    $prev = $arr;
}
for($i = 0; $i<$max; $i++){
    echo "Для [$i] совпадений: ".$stat[$i].PHP_EOL;
}
Для [0] совпадений: 9999
Для [1] совпадений: 5026
Для [2] совпадений: 3354
Для [3] совпадений: 2476
Для [4] совпадений: 1979
Для [5] совпадений: 1679
Для [6] совпадений: 1429
Для [7] совпадений: 1251
Для [8] совпадений: 1172
Для [9] совпадений: 964
Для [10] совпадений: 864
 

shift_on_line

Новичок
Мы выбираем случайное число в диапазоне от нуля до нашей внутренней переменной $i, в которой, как мы дальше увидим, хранится порядковый номер текущей строки: rand(0, $i++). Затем мы проверяем, не равно ли это случайное число нулю, и если равно, то записываем в переменную $string значение текущей строки (которая хранится в $tmp). Так как в функции rand() в качестве максимального значения у нас указано $i++, то сервер сначала выберет какое-то значение от 0 до $i, a потом уже увеличит $i на единицу - и, следовательно, при следующем проходе цикла $i будет снова содержать порядковый номер текущей строки (отсчет ведется с нуля).

Вероятность выпадения какого-то числа (в нашем случае - нуля) из N имеющихся равна 1/N. А это означает, что при первом проходе цикла (нулевая строка) мы с вероятностью, равной единице, сохраним в переменной $string эту строку. При втором проходе - с вероятностью в 50% в переменную $string запишется первая строка. При третьем - с вероятностью 33% запишется вторая строка. И так далее. Можете сами убедиться, что такой подход действительно выбирает случайную строку из файла, и при этом память тратится только на хранение текущей строки!
 

hell0w0rd

Продвинутый новичок
Какая-то хрень как по мне)
Надо взять случайное число от 0 до размера файла, сместить указатель на это число, дойти до следующего \n и взять строку до следующего \n. Вот будет по настоящему случайная строка, а не эта хрень)
 

Вурдалак

Продвинутый новичок
Какая-то хрень как по мне)
Надо взять случайное число от 0 до размера файла, сместить указатель на это число, дойти до следующего \n и взять строку до следующего \n. Вот будет по настоящему случайная строка, а не эта хрень)
10 строк по 5 символов и 1 на 1024. И первая строка вообще выпадать не будет.

По-хорошему, надо иметь индекс, который хранит offset'ы переводов строк.
 

hell0w0rd

Продвинутый новичок
эта не менее случайная
ага) сгенерируй файл больше 10 строчек и потести два способа) второй как минимум быстрее)
PHP:
function goodKeywordRand($file)
{
    $seek = mt_rand(0, filesize($file));
    $f = fopen($file, 'r');
    fseek($f, $seek);
    for($str = null; $str != PHP_EOL; $str = fread($f, 1));
    $str = '';
    for($temp = null, $i = 0; $temp!=PHP_EOL; $temp = fread($f, 1), $i++ ){
        if($i==8196){
            break;
        }
        $str .= $temp;
    }
    fclose($f);
    return $str;
}

$time = microtime(true);
echo goodKeywordRand(__DIR__.'/data.txt').PHP_EOL;
var_dump(microtime(true) - $time);

$time = microtime(true);
echo keyword_rand(__DIR__.'/data.txt').PHP_EOL;
var_dump(microtime(true) - $time);
PHP:
$ php test.php 
102462698d2acf5b5f8d604e6da4c78e
double(0.00044012069702148)
3d533754dc7fa646824cf520e294d264
double(41.819302082062)
в data.txt миллион md5(microtime());
Вурдалак
Да, по хорошему надо) Но согласись второй метод правильнее, нежели первый) Можно добавить проверку, если $seek<8196 - не трогать указатель)
 

Вурдалак

Продвинутый новичок
Есть ещё вариант с хранением строк фиксированной длины (дополнением пробелами или ещё как-то). Зная длину, можно легко вычислить количество строк и случайный offset.
 

fixxxer

К.О.
Партнер клуба
GusakovNick
лучше все же не по одному байту читать, а пачками взад-вперед до появления \n ;)
 
Сверху