Мне нужен портативный, последовательный генератор псевдослучайных чисел

Я пишу функцию kid sister encryption, и мне нужен PRNG, который дает согласованные результаты по всем операционным системам (так что нет математики с плавающей запятой, используя оборудования или программного обеспечения на уровне системы). Было бы неплохо, но не обязательно, потому что PRNG имел период дольше, чем 2 30.

В настоящее время я использую 32-битный Xorshift:

#!/usr/bin/perl

use strict;
use warnings;

{
    use integer; #use integer math
    my $x = 123456789;
    my $y = 362436069;
    my $w = 88675123; 
    my $z = 521288629;

    sub set_random_seed {
        $w = shift;
    }

    sub random { 
        my $t = $x ^ ($x << 11);
        $x = $y;
        $y = $z;
        $z = $w;
        my $rand = $w = ($w ^ ($w >> 19)) ^ ($t ^ ($t >> 8)); 
        return $rand % 256; #scale it back to a byte at a time
    }
}

set_random_seed(5);
print map { random(), "\n" } 1 .. 10;

Но я беспокоюсь, потому что я действительно не понимаю, как это работает. Например, исходный источник не смог установить семя, поэтому я добавил его, но не знаю, выбрал ли я правильную переменную для семени.

Итак, все это сводится к

  • Знаете ли вы о модуле CPAN, который соответствует моим потребностям?
  • Если нет, знаете ли вы алгоритм, который соответствует моим потребностям?

Ответ 2

Попробуйте использовать LFSR - Линейный регистр сдвига обратной связи.. T В первой ссылке на внешних ссылках есть все необходимое для создания любого количества бит случайности. Самое приятное в этом состоит в том, что его легко реализовать и может быть выполнено с использованием целочисленной математики.

Я использовал его с успехом в проекте 8051. С perl это будет легко.

Update:

Вот быстрая реализация perl из 8-битного LFSR:

use strict;
use warnings;

use List::Util qw(reduce);
use vars qw($a $b);

print 'Taps: ', set_taps( 8, 7, 2, 1 ), "\n";
print 'Seed: ', seed_lfsr( 1 ), "\n";
print read_lfsr(), "\n" for 1..10;

BEGIN {
    my $tap_mask;
    my $lfsr = 0;

    sub read_lfsr {
        $lfsr = ($lfsr >> 1) ^ (-($lfsr & 1) & $tap_mask );

        return $lfsr;
    }

    sub seed_lfsr {
        $lfsr = shift || 0;
        $lfsr &= 0xFF;
    }

    sub set_taps {
        my @taps = @_;

        $tap_mask = reduce { $a + 2**$b } 0, @taps;

        $tap_mask >>= 1;

        $tap_mask &= 0xFF;

        return $tap_mask;
    }
}

Этот код является всего лишь прототипом. Если бы я хотел использовать его в процессе производства, я бы, вероятно, обернул его в объект и сделал размер регистра настраиваемым. Тогда мы избавимся от этих досадных общих переменных.