Nicht Final, aber funktional

main
Gideon Regehr 2023-05-08 14:15:39 +02:00
parent 220c64ae5d
commit 2f725cdde4
4 changed files with 375 additions and 0 deletions

View File

@ -0,0 +1,58 @@
from TotOnline import TotOnline
from StartUpTest import StartUPTest
class Main:
def __init__(self):
print("Main class instantiated")
if __name__ == "__main__":
filename = "resources/SmallTest.txt"
with open(filename, "rb") as f:
binary_data = f.read().strip()
filename = "resources/data2.txt"
with open(filename, "rb") as f:
binary_data2 = f.read().strip()
# Call the StarUpTest
print("StartUp:")
result = StartUPTest.monobit_test(binary_data2)
print("p_value:", result[0])
print("test passed (p_value >= 0.01):", result[1])
result = StartUPTest.test5(binary_data2)
print("Z_tau:", result[0])
print("test passed (Z_tau > 2326 and Z_tau < 2674):", result[1])
# Call the TotalFailure-Test
print("Total Failure:")
result = TotOnline.total_failure(binary_data, pattern_length=10)
print("p_value:", result[0])
print("test passed (p_value >= 0.01):", result[1])
# Call the Online Test
print("Monobit:")
result = TotOnline.monobit_test(binary_data)
print("p_value:", result[0])
print("test passed (p_value >= 0.01):", result[1])
print("Block Frequency:")
result = TotOnline.block_frequency(binary_data)
print("p_value:", result[0])
print("test passed (p_value >= 0.01):", result[1])
print("Run:")
result = TotOnline.run_test(binary_data)
print("p_value:", result[0])
print("test passed (p_value >= 0.01):", result[1])
print("Longest Run:")
result = TotOnline.longest_one_block_test(binary_data)
print("p_value:", result[0])
print("test passed (p_value >= 0.01):", result[1])

View File

@ -0,0 +1,2 @@
Die Main Klasse fällt nacher weg und wird vollständig im restlichen Code zur Erstellung und Verarbeitung der Zufallszahlen integriert.
Aktuell werden die Daten für die Tests aus zwei verschiedenen Dateien eingelesen, da der Startup Test eine deutlich größere Menge an Zahlen benötigt (hier mindestens 20.000 Bits der Total Failure/Online Test nur mindestens 128 Bits)

View File

@ -0,0 +1,88 @@
from math import fabs as fabs
from math import sqrt as sqrt
from scipy.special import erfc as erfc
class StartUPTest:
@staticmethod
def monobit_test(binary_data: str):
length_of_bit_string = len(binary_data)
# Variable for S(n)
count = 0
# Iterate each bit in the string and compute for S(n)
for bit in binary_data:
if bit == 48:
# If bit is 0, then -1 from the S(n)
count -= 1
elif bit == 49:
# If bit is 1, then +1 to the S(n)
count += 1
# Compute the test statistic
sObs = count / sqrt(length_of_bit_string)
# Compute p-Value
p_value = erfc(fabs(sObs) / sqrt(2))
# return a p_value and randomness result
return (p_value, (p_value >= 0.01))
@staticmethod
def test5(binary_data: str):
ShiftFeld = [0] * 5000
MaxKorrFeld = [0] * 5000
# Fill BitFeldB with data
for tau in range(1, 5001):
Z_tau = 0
for i in range(5000):
Z_tau += binary_data[i] ^ binary_data[i + tau]
ShiftFeld[tau - 1] = Z_tau
#Debugging
#for i in range(5000):
# print(ShiftFeld[i], end=' ')
# Find the index of the maximum deviation from 2500
max_deviation = 0
for tau in range(5000):
deviation = abs(ShiftFeld[tau] - 2500)
if deviation > max_deviation:
max_deviation = deviation
# Find all indices with the maximum deviation
j = 0
for tau in range(5000):
deviation = abs(ShiftFeld[tau] - 2500)
if deviation == max_deviation:
MaxKorrFeld[j] = tau
j += 1
print("Maximale Z_tau-Abweichung von 2500:", max_deviation)
print("Aufgetreten für Shifts:")
for k in range(j):
print("Shift:", MaxKorrFeld[k] + 1)
tau = MaxKorrFeld[0]
Z_tau = 0
for i in range(10000, 15000):
Z_tau += StartUPTest.charToInt(i, binary_data) ^ StartUPTest.charToInt(i + tau + 1, binary_data)
tau += 1
ok = Z_tau > 2326 and Z_tau < 2674
return (Z_tau, ok)
@staticmethod
def charToInt(index, binary_data: str):
value = 0
if binary_data[index] == 49:
value = 1
else:
value = 0
return value

View File

@ -0,0 +1,227 @@
from math import log as log
from numpy import zeros as zeros
from math import fabs as fabs
from math import floor as floor
from math import sqrt as sqrt
from scipy.special import erfc as erfc
from scipy.special import gammaincc as gammaincc
class TotOnline:
@staticmethod
def total_failure(binary_data: str, pattern_length=10):
length_of_binary_data = len(binary_data)
# Augment the n-bit sequence to create n overlapping m-bit sequences by appending m-1 bits
# from the beginning of the sequence to the end of the sequence.
binary_data += binary_data[:pattern_length + 1:]
# Get max length one patterns for m, m-1, m-2
max_pattern = ''
for i in range(pattern_length + 2):
max_pattern += '1'
# Keep track of each pattern's frequency (how often it appears)
vobs_01 = zeros(int(max_pattern[0:pattern_length:], 2) + 1)
vobs_02 = zeros(int(max_pattern[0:pattern_length + 1:], 2) + 1)
for i in range(length_of_binary_data):
# Work out what pattern is observed
vobs_01[int(binary_data[i:i + pattern_length:], 2)] += 1
vobs_02[int(binary_data[i:i + pattern_length + 1:], 2)] += 1
# Calculate the test statistics and p values
vobs = [vobs_01, vobs_02]
sums = zeros(2)
for i in range(2):
for j in range(len(vobs[i])):
if vobs[i][j] > 0:
sums[i] += vobs[i][j] * log(vobs[i][j] / length_of_binary_data)
sums /= length_of_binary_data
ape = sums[0] - sums[1]
xObs = 2.0 * length_of_binary_data * (log(2) - ape)
p_value = gammaincc(pow(2, pattern_length - 1), xObs / 2.0)
return (p_value, (p_value >= 0.01))
@staticmethod
def monobit_test(binary_data: str):
length_of_bit_string = len(binary_data)
# Variable for S(n)
count = 0
# Iterate each bit in the string and compute for S(n)
for bit in binary_data:
if bit == 48:
# If bit is 0, then -1 from the S(n)
count -= 1
elif bit == 49:
# If bit is 1, then +1 to the S(n)
count += 1
# Compute the test statistic
sObs = count / sqrt(length_of_bit_string)
# Compute p-Value
p_value = erfc(fabs(sObs) / sqrt(2))
# return a p_value and randomness result
return (p_value, (p_value >= 0.01))
@staticmethod
def block_frequency(binary_data: str, block_size=128):
length_of_bit_string = len(binary_data)
if length_of_bit_string < block_size:
block_size = length_of_bit_string
# Compute the number of blocks based on the input given. Discard the remainder
number_of_blocks = floor(length_of_bit_string / block_size)
if number_of_blocks == 1:
# For block size M=1, this test degenerates to test 1, the Frequency (Monobit) test.
return TotOnline.monobit_test(binary_data[0:block_size])
# Initialized variables
block_start = 0
block_end = block_size
proportion_sum = 0.0
# Create a for loop to process each block
for counter in range(number_of_blocks):
# Partition the input sequence and get the data for block
block_data = binary_data[block_start:block_end]
# Determine the proportion 蟺i of ones in each M-bit
one_count = 0
for bit in block_data:
if bit == 49:
one_count += 1
# compute π
pi = one_count / block_size
# Compute Σ(πi -½)^2.
proportion_sum += pow(pi - 0.5, 2.0)
# Next Block
block_start += block_size
block_end += block_size
# Compute 4M Σ(πi -½)^2.
result = 4.0 * block_size * proportion_sum
# Compute P-Value
p_value = gammaincc(number_of_blocks / 2, result / 2)
return (p_value, (p_value >= 0.01))
@staticmethod
def run_test(binary_data: str):
one_count = 0
vObs = 0
length_of_binary_data = len(binary_data)
# Predefined tau = 2 / sqrt(n)
tau = 2 / sqrt(length_of_binary_data)
# Step 1 - Compute the pre-test proportion πof ones in the input sequence: π = Σjεj / n
one_count = binary_data.count(49)
pi = one_count / length_of_binary_data
# Step 2 - If it can be shown that absolute value of (π - 0.5) is greater than or equal to tau
# then the run test need not be performed.
if abs(pi - 0.5) >= tau:
return (0.0000)
else:
# Step 3 - Compute vObs
for item in range(1, length_of_binary_data):
if binary_data[item] != binary_data[item - 1]:
vObs += 1
vObs += 1
# Step 4 - Compute p_value = erfc((|vObs 2nπ * (1π)|)/(2 * sqrt(2n) * π * (1π)))
p_value = erfc(abs(vObs - (2 * (length_of_binary_data) * pi * (1 - pi))) / (2 * sqrt(2 * length_of_binary_data) * pi * (1 - pi)))
return (p_value, (p_value > 0.01))
@staticmethod
def longest_one_block_test(binary_data: str):
length_of_binary_data = len(binary_data)
# print('Length of binary string: ', length_of_binary_data)
# Initialized k, m. n, pi and v_values
if length_of_binary_data < 128:
# Not enough data to run this test
return (0.00000, 'Error: Not enough data to run this test')
elif length_of_binary_data < 6272:
k = 3
m = 8
v_values = [1, 2, 3, 4]
pi_values = [0.2148, 0.3672, 0.2305, 0.1875]
elif length_of_binary_data < 750000:
k = 5
m = 128
v_values = [4, 5, 6, 7, 8, 9]
pi_values = [0.1174, 0.2430, 0.2493, 0.1752, 0.1027, 0.1124]
else:
# If length_of_bit_string > 750000
k = 6
m = 10000
v_values = [10, 11, 12, 13, 14, 15, 16]
pi_values = [0.0882, 0.2092, 0.2483, 0.1933, 0.1208, 0.0675, 0.0727]
number_of_blocks = floor(length_of_binary_data / m)
block_start = 0
block_end = m
xObs = 0
# This will intialized an array with a number of 0 you specified.
frequencies = zeros(k + 1)
# print('Number of Blocks: ', number_of_blocks)
for count in range(number_of_blocks):
block_data = binary_data[block_start:block_end]
max_run_count = 0
run_count = 0
# This will count the number of ones in the block
for bit in block_data:
if bit == 49:
run_count += 1
max_run_count = max(max_run_count, run_count)
else:
max_run_count = max(max_run_count, run_count)
run_count = 0
max(max_run_count, run_count)
#print('Block Data: ', block_data, '. Run Count: ', max_run_count)
if max_run_count < v_values[0]:
frequencies[0] += 1
for j in range(k):
if max_run_count == v_values[j]:
frequencies[j] += 1
if max_run_count > v_values[k - 1]:
frequencies[k] += 1
block_start += m
block_end += m
# print("Frequencies: ", frequencies)
# Compute xObs
for count in range(len(frequencies)):
xObs += pow((frequencies[count] - (number_of_blocks * pi_values[count])), 2.0) / (
number_of_blocks * pi_values[count])
p_value = gammaincc(float(k / 2), float(xObs / 2))
return (p_value, (p_value > 0.01))