capricious 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +5 -0
- data/VERSION +1 -1
- data/lib/capricious.rb +2 -0
- data/lib/capricious/generic_prng.rb +53 -0
- data/lib/capricious/lfsr.rb +4 -3
- data/lib/capricious/mwc5.rb +71 -0
- data/lib/capricious/poisson.rb +46 -0
- data/lib/capricious/sample_sink.rb +4 -4
- data/lib/capricious/uniform.rb +5 -18
- data/spec/poisson_spec.rb +27 -0
- data/spec/uniform_spec.rb +19 -19
- metadata +7 -2
data/CHANGES
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/lib/capricious.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
# capricious/generic_prng.rb: generic PRNG mixin with selectable source-randomness
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton <willb@redhat.com>
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'capricious/lfsr'
|
20
|
+
require 'capricious/mwc5'
|
21
|
+
require 'capricious/sample_sink'
|
22
|
+
|
23
|
+
module Capricious
|
24
|
+
module PRNG
|
25
|
+
|
26
|
+
def initialize(seed=nil, policy=MWC5, keep_stats=false)
|
27
|
+
prng_initialize(seed, policy, keep_stats)
|
28
|
+
end
|
29
|
+
|
30
|
+
def prng_initialize(seed=nil, policy=MWC5, keep_stats=false)
|
31
|
+
@prng = policy.new_with_seed(seed)
|
32
|
+
@seed = @prng.seed
|
33
|
+
@aggregate = SampleSink.new if keep_stats
|
34
|
+
end
|
35
|
+
|
36
|
+
def next
|
37
|
+
val = next_value
|
38
|
+
@aggregate << val if @aggregate
|
39
|
+
val
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset(seed=nil)
|
43
|
+
@prng.reset(seed)
|
44
|
+
@aggregate = SampleSink.new if @aggregate
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.included(base)
|
48
|
+
base.class_eval do
|
49
|
+
attr_reader :aggregate, :seed
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/capricious/lfsr.rb
CHANGED
@@ -47,13 +47,13 @@ module Capricious
|
|
47
47
|
reset
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
50
|
+
def next_i
|
51
51
|
shift_reg
|
52
52
|
@reg
|
53
53
|
end
|
54
54
|
|
55
55
|
def next_f
|
56
|
-
|
56
|
+
next_i
|
57
57
|
@reg.quo(@ns::MASK.to_f)
|
58
58
|
end
|
59
59
|
|
@@ -66,7 +66,8 @@ module Capricious
|
|
66
66
|
module SixtyFourBitShifter
|
67
67
|
MASK = 0xffffffffffffffff
|
68
68
|
SIZE = 64
|
69
|
-
BITS = [64,63,61,60]
|
69
|
+
# BITS = [64,63,61,60]
|
70
|
+
BITS = [64,4,3,1]
|
70
71
|
BITSELECT = BITS.map {|bit| "@reg[#{SIZE-bit}]"}.join("^")
|
71
72
|
|
72
73
|
class_eval <<-SHIFT_REG
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# capricious/mwc5.rb: 32-bit multiply-with-carry PRNG
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton <willb@redhat.com>
|
6
|
+
# Algorithm due to George Marsaglia: http://groups.google.com/group/comp.lang.c/msg/e3c4ea1169e463ae
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
|
20
|
+
require 'capricious/lfsr'
|
21
|
+
|
22
|
+
module Capricious
|
23
|
+
class MWC5
|
24
|
+
attr_reader :seed, :seeds
|
25
|
+
def MWC5.new_with_seed(seed)
|
26
|
+
MWC5.new(seed)
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(seed=nil)
|
30
|
+
reset(seed)
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset(seed=nil)
|
34
|
+
@seed ||= (seed || Time.now.utc.to_i)
|
35
|
+
unless @seeds
|
36
|
+
seeder = LFSR.new_with_seed(@seed)
|
37
|
+
@seeds = [seeder.next_i & 0xffffffff]
|
38
|
+
4.times do
|
39
|
+
9.times do
|
40
|
+
# the observed lag-10 autocorrelation of the LFSRs is low
|
41
|
+
seeder.next_i
|
42
|
+
end
|
43
|
+
@seeds << (seeder.next_i & 0xffffffff)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@x,@y,@z,@w,@v = @seeds
|
48
|
+
end
|
49
|
+
|
50
|
+
def next_i
|
51
|
+
shift_ks
|
52
|
+
end
|
53
|
+
|
54
|
+
def next_f
|
55
|
+
next_i.quo(0xffffffff.to_f)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def shift_ks
|
60
|
+
# XXX: this is ugly, but it has to be to avoid coercing things into bignums
|
61
|
+
t=(@x^(@x>>7)) & 0xffffffff
|
62
|
+
@x=@y
|
63
|
+
@y=@z
|
64
|
+
@z=@w
|
65
|
+
@w=@v
|
66
|
+
@v=(@v^(@v<<6))^(t^(t<<13)) & 0xffffffff
|
67
|
+
yy = ((@y & 0x7fffffff << 1) + 1) & 0xffffffff
|
68
|
+
(yy * @v) & 0xffffffff;
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# capricious/poisson.rb: Poisson-distribution PRNG, with selectable source-randomness policy
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton <willb@redhat.com>
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'capricious/generic_prng'
|
20
|
+
|
21
|
+
module Capricious
|
22
|
+
class Poisson
|
23
|
+
include PRNG
|
24
|
+
|
25
|
+
attr_reader :z
|
26
|
+
|
27
|
+
def initialize(l, seed=nil, policy=MWC5, keep_stats=false)
|
28
|
+
@z = Math.exp(-l)
|
29
|
+
prng_initialize(seed, policy, keep_stats)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
# Algorithm 369, CACM, January 1970
|
34
|
+
def next_value
|
35
|
+
k = 0
|
36
|
+
|
37
|
+
t = @prng.next_f
|
38
|
+
while t > self.z
|
39
|
+
k = k + 1
|
40
|
+
t = t * @prng.next_f
|
41
|
+
end
|
42
|
+
|
43
|
+
k
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -19,13 +19,13 @@
|
|
19
19
|
module Capricious
|
20
20
|
class SampleSink
|
21
21
|
|
22
|
-
attr_reader :min, :max, :count, :mean
|
22
|
+
attr_reader :min, :max, :count, :mean, :variance
|
23
23
|
|
24
24
|
def initialize
|
25
25
|
@min = nil
|
26
26
|
@max = nil
|
27
27
|
@count = 0
|
28
|
-
@
|
28
|
+
@variance = 0.0
|
29
29
|
@mean = 0.0
|
30
30
|
@sum_x2 = 0.0
|
31
31
|
end
|
@@ -42,7 +42,7 @@ module Capricious
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def stddev
|
45
|
-
Math::sqrt(@
|
45
|
+
Math::sqrt(@variance)
|
46
46
|
end
|
47
47
|
|
48
48
|
private
|
@@ -56,7 +56,7 @@ module Capricious
|
|
56
56
|
dev = sample - @mean
|
57
57
|
@mean += (dev / @count)
|
58
58
|
@sum_x2 += (dev * (sample - @mean))
|
59
|
-
@
|
59
|
+
@variance = @sum_x2 / @count
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
data/lib/capricious/uniform.rb
CHANGED
@@ -16,28 +16,15 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
require 'capricious/
|
20
|
-
require 'capricious/sample_sink'
|
19
|
+
require 'capricious/generic_prng'
|
21
20
|
|
22
21
|
module Capricious
|
23
22
|
class Uniform
|
24
|
-
|
23
|
+
include PRNG
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
@
|
29
|
-
@aggregate = SampleSink.new if keep_stats
|
30
|
-
end
|
31
|
-
|
32
|
-
def next
|
33
|
-
val = @prng.next_f
|
34
|
-
@aggregate << val if @aggregate
|
35
|
-
val
|
36
|
-
end
|
37
|
-
|
38
|
-
def reset(seed=nil)
|
39
|
-
@prng.reset(seed)
|
40
|
-
@aggregate = SampleSink.new if @aggregate
|
25
|
+
private
|
26
|
+
def next_value
|
27
|
+
@prng.next_f
|
41
28
|
end
|
42
29
|
end
|
43
30
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
module Capricious
|
4
|
+
LAMBDA = 8
|
5
|
+
SAMPLE_COUNT = 200
|
6
|
+
|
7
|
+
describe Poisson do
|
8
|
+
def generate_samples(count=SAMPLE_COUNT)
|
9
|
+
count.times {@poisson.next}
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@poisson = Capricious::Poisson.new(LAMBDA, nil, MWC5, true)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should generate distributed numbers in a Poisson distribution with lambda #{LAMBDA}, as judged by mean and variance estimates" do
|
17
|
+
generate_samples(10000)
|
18
|
+
@poisson.aggregate.mean.should be_close(LAMBDA, 0.05)
|
19
|
+
@poisson.aggregate.variance.should be_close(LAMBDA, 0.15)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should generate the same sequence given the same seed" do
|
23
|
+
@poisson2 = Poisson.new(LAMBDA, @poisson.seed, MWC5, true)
|
24
|
+
SAMPLE_COUNT.times { @poisson2.next.should == @poisson.next }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/uniform_spec.rb
CHANGED
@@ -3,32 +3,32 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
3
|
module Capricious
|
4
4
|
EXPECTED_MEAN = 0.5
|
5
5
|
EXPECTED_STDDEV = 0.288675134594813
|
6
|
-
SAMPLE_COUNT =
|
6
|
+
SAMPLE_COUNT = 20000
|
7
7
|
|
8
8
|
describe Uniform do
|
9
|
-
def generate_samples(count=SAMPLE_COUNT)
|
9
|
+
def generate_samples(policy=MWC5, count=SAMPLE_COUNT)
|
10
|
+
@uniform = Capricious::Uniform.new(nil, policy, true)
|
10
11
|
count.times {@uniform.next}
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@uniform.aggregate.max.should <= 1.0
|
20
|
-
@uniform.aggregate.min.should > 0.0
|
21
|
-
end
|
14
|
+
[LFSR,MWC5].each do |policy|
|
15
|
+
it "should, given policy #{policy.name}, generate numbers in the range (0,1]" do
|
16
|
+
generate_samples(policy)
|
17
|
+
@uniform.aggregate.max.should <= 1.0
|
18
|
+
@uniform.aggregate.min.should > 0.0
|
19
|
+
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
it "should, given policy #{policy.name}, generate uniformly-distributed numbers in the range (0,1], as judged by mean and variance estimates" do
|
22
|
+
generate_samples(policy)
|
23
|
+
@uniform.aggregate.mean.should be_close(EXPECTED_MEAN, 0.01)
|
24
|
+
@uniform.aggregate.stddev.should be_close(EXPECTED_STDDEV, 0.01)
|
25
|
+
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
it "should, given policy #{policy.name}, generate the same sequence given the same seed" do
|
28
|
+
@uniform = Uniform.new(nil, policy, false)
|
29
|
+
@uniform2 = Uniform.new(nil, policy, false)
|
30
|
+
(SAMPLE_COUNT/10).times { @uniform2.next.should == @uniform.next }
|
31
|
+
end
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capricious
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Will Benton
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-03-
|
12
|
+
date: 2010-03-12 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,9 +40,13 @@ files:
|
|
40
40
|
- Rakefile
|
41
41
|
- VERSION
|
42
42
|
- lib/capricious.rb
|
43
|
+
- lib/capricious/generic_prng.rb
|
43
44
|
- lib/capricious/lfsr.rb
|
45
|
+
- lib/capricious/mwc5.rb
|
46
|
+
- lib/capricious/poisson.rb
|
44
47
|
- lib/capricious/sample_sink.rb
|
45
48
|
- lib/capricious/uniform.rb
|
49
|
+
- spec/poisson_spec.rb
|
46
50
|
- spec/spec.opts
|
47
51
|
- spec/spec_helper.rb
|
48
52
|
- spec/uniform_spec.rb
|
@@ -75,5 +79,6 @@ signing_key:
|
|
75
79
|
specification_version: 3
|
76
80
|
summary: Pseudorandom number generator classes and support code
|
77
81
|
test_files:
|
82
|
+
- spec/poisson_spec.rb
|
78
83
|
- spec/spec_helper.rb
|
79
84
|
- spec/uniform_spec.rb
|