Сгенерировать 0 и 1, псевдослучайную, длинную строку из 16 символов

Мне нужно сгенерировать 16-символьную строку (из хэша SHA1), которая содержит только 0 и 1, с вероятностью 50% (статистически в большинстве случаев такое же количество 1 в строке, как и количество 0).

Итак, я написал бенчмарк и попытался преобразовать каждый символ $hash в двоичный. Результаты плохие, я имею в виду, что если я добавляю ведущие нули к двоичному преобразованному хэшу, правильная вероятность далека от правильной. Когда я не добавляю ведущие нули к двоичному преобразованию, вероятность близка к правильной:

Percentage all 0 or all 1: 0.0012%
Percentage all 0 or all 1 except 1 character : 0.0146%
Percentage all 0 or all 1 except 2 characters: 0.0812%

Но это все еще далеко от истинно правильной вероятности того, что приведенный ниже код должен произвести:

Percentage all 0 or all 1: 0.003%
Percentage all 0 or all 1 except 1 character : 0.048%
Percentage all 0 or all 1 except 2 characters: 0.376%

Как узнать правильную вероятность? Я изменил двоичное преобразование на простое mt_rand(0,1) шестнадцать раз (и другие подтверждающие тесты).

Он должен быть сгенерирован из хэша sha1, чтобы быть детерминированным этим хешем. Кто-нибудь знает, как исправить мой код, чтобы получить правильные результаты вероятности? Пробовал уже 10 часов подряд.

    function binary($text){
            $list = '';
            $temp = '';
            $i = 0;
            while ($i < 16){
                    if (is_numeric($text[$i])){
                            $list .= decbin( $text[$i] );//sprintf( "%08d", decbin( $text[$i] ));
                    } else {
                            $temp = ord($text[$i]);
                            $list .= decbin( $temp );
    //                      $list .= sprintf( "%08d", decbin( $temp ));// substr("00000000",0,8 - strlen($temp)) . $temp;
                    }
            $i++;
            }
            return $list;
    }

    $y = 0;
    $trafien = 0;
    $trafien1= 0;
    $trafien2= 0;
    $max = 500000;
    while ($y < $max){

    $time = uniqid()  . mt_rand(1,999999999999);
    $seed = 'eqm2890rmn9ou8nr9q2';
    $hash = sha1($time . $seed);

    $last4 = substr($hash, 0, 40);
    $binary =  binary($last4);
    $final = substr($binary, 0,16);

    $ile = substr_count($final, '0');
    $ile2= substr_count($final, '1');
    if ($ile == 16 || $ile2 == 16){
        echo "\n".$last4 ." " . 'binary: '. $binary .' final: '. $final;
        $trafien += 1;
    }

    if ($ile == 15 || $ile2 == 15){
        $trafien1 += 1;
    }

    if ($ile == 14 || $ile2 == 14){
        $trafien2 += 1;
    }

$y++;
}

$procent = ($trafien * 100)  / $max;
$procent1= ($trafien1 * 100) / $max;
$procent2= ($trafien2 * 100) / $max;
echo "\nPercentage all 0 or all 1: ". $procent . "%";
echo "\nPercentage all 0 or all 1 except 1 character : ". $procent1 . "%";
echo "\nPercentage all 0 or all 1 except 2 characters: ". $procent2 . "%";

person John    schedule 26.04.2015    source источник
comment
Использование uniqid(), вероятно, не подходит для генератора случайных чисел, поскольку оно основано на микровремени.   -  person Devon    schedule 27.04.2015
comment
@Devon uniqid() используется только для целей тестирования, в реальном коде хэш генерируется другим способом.   -  person John    schedule 27.04.2015
comment
Почему вы генерируете хэш по-разному в тесте?   -  person Devon    schedule 27.04.2015
comment
потому что способ получения хэша здесь не имеет значения, он должен быть уникальным в тесте, и у меня нет, например, user_id в тесте, поэтому я использую mt_rand + uniqid()   -  person John    schedule 27.04.2015


Ответы (2)


Думаю, я не понимаю, почему вам нужно изобретать колесо здесь или использовать sha1(), когда вы используете только 4 символа и конвертируете в двоичный код. Кажется, это работает нормально:

$n = null;
for ($i=1; $i<=16; $i++) {
  $n .= mt_rand(0,1);
}

Вот тестовый скрипт, который я написал:

// $app for number of appearances
$app[0] = 0;
$app[1] = 0;
$sample = 10000;

for ($t=1; $t<=$sample; $t++) {

  $n = null;
  for ($i=1; $i<=16; $i++) {
    $n .= mt_rand(0,1);
  }

  $app[0] += substr_count($n, 0);
  $app[1] += substr_count($n, 1);
}

print_r($app);
echo "Probability of 0: ".($app[0] / ($sample * 16))."\n";
echo "Probability of 1: ".($app[1] / ($sample * 16))."\n";

Вывод с размером выборки 10000:

Array
(
    [0] => 80079
    [1] => 79921
)
Probability of 0: 0.50049375
Probability of 1: 0.49950625

Вывод с размером выборки 100000:

Array
(
    [0] => 799390
    [1] => 800610
)
Probability of 0: 0.49961875
Probability of 1: 0.50038125
person Devon    schedule 26.04.2015
comment
Я написал в своем посте об использовании mt_rand(0,1), вы не читали мой пост :P, также дважды сказали, что я должен сделать это с помощью хэша. Он должен быть детерминирован по хешу. Поверьте мне, если бы я мог, я бы сделал это простым способом :/ - person John; 27.04.2015
comment
Я знаю, что ты это сказал, но не объяснил, почему. Объяснение того, почему вам нужно, чтобы он был детерминированным по хешу, может иметь больше смысла. - person Devon; 27.04.2015
comment
Потому что у меня должно быть доказательство того, что я не придумал эти числа (0/1), и они были созданы независимым детерминированным хешем. Тогда любой может проверить, действительно ли эти числа были созданы этим хэшем. - person John; 27.04.2015
comment
Какова конечная цель этого кодекса? Я не понимаю, как использование последних 4 символов хэша sha1 добавляет какую-либо проверку. Как они могут проверить исходный хэш? Вы не можете восстановить его из последних 16 бит, даже если бы вы могли, почему это отличается от случайных 16 бит из mt_rand? Это домашнее задание или что? Я не вижу смысла в другом. - person Devon; 27.04.2015
comment
Нет, это не домашнее задание. Пользователю не нужно реконструировать хэш, он знает, как этот хэш создается (в живом коде нет случайности в создании хэша). Таким образом, он может создать целый хэш и использовать известный алгоритм (этот мой выше, который еще не работает должным образом), чтобы проверить эти 16 символов длиной 0/1 :) - person John; 27.04.2015
comment
@Джон, хорошо. Я предлагаю вам обновить свой исходный вопрос, объясняя это, чтобы вы могли получить более ценный вклад. Прямо сейчас, когда появляется вопрос, вы просто пытаетесь получить случайную 16-битную двоичную строку, которая, похоже, совсем не то, что вам действительно нужно. - person Devon; 27.04.2015
comment
спасибо за участие :), вы единственный человек, который пытался помочь - person John; 27.04.2015

Хорошо, я решил это, я переутомляюсь из-за недостатка знаний. Вы можете использовать прямой необработанный двоичный вывод функции sha1 sha1(data, true). Тогда у вас есть настоящая детерминированная случайность 0/1 :)

person John    schedule 27.04.2015