Загрузка данных
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace BethPiperStopAndGo
{
/// <summary>
/// Класс сдвигового регистра с линейной обратной связью (LFSR)
/// </summary>
public class Lfsr
{
private readonly int _length;
private readonly int[] _taps; // Отводы (индексы от 1 до N)
private ulong _state;
public Lfsr(int length, int[] taps, ulong initialState = 0b1)
{
_length = length;
_taps = taps;
// Состояние не должно быть нулевым
_state = initialState == 0 ? 1 : initialState;
}
/// <summary>
/// Возвращает текущий младший (выходной) бит
/// </summary>
public byte OutputBit => (byte)(_state & 1);
/// <summary>
/// Выполняет один шаг сдвига регистра
/// </summary>
public byte Step()
{
byte outBit = (byte)(_state & 1);
// Вычисление бита обратной связи (XOR отводов)
ulong feedback = 0;
foreach (int tap in _taps)
{
if (tap > 0) // tap == 0 соответствует константе 1 в примитивном многочлене
{
feedback ^= (_state >> (_length - tap)) & 1;
}
}
// Сдвиг вправо и занесение обратной связи в старший бит
_state = (_state >> 1) | (feedback << (_length - 1));
return outBit;
}
}
/// <summary>
/// Генератор «стоп-пошел» Бетта-Пайпера (Вариант 4)
/// </summary>
public class BethPiperGenerator
{
private readonly Lfsr _lfsr1; // (32, 7, 5, 3, 2, 1, 0)
private readonly Lfsr _lfsr2; // (12, 6, 4, 1, 0)
private readonly Lfsr _lfsr3; // (28, 3, 0)
public BethPiperGenerator(ulong seed1 = 0xACE1, ulong seed2 = 0b101, ulong seed3 = 0x12345)
{
// Задание регистров по отводам из методички
_lfsr1 = new Lfsr(32, new[] { 32, 7, 5, 3, 2, 1, 0 }, seed1);
_lfsr2 = new Lfsr(12, new[] { 12, 6, 4, 1, 0 }, seed2);
_lfsr3 = new Lfsr(28, new[] { 28, 3, 0 }, seed3);
}
public byte Step()
{
// 1. Управляющий LFSR1 сделает шаг и выдаст управление a(t)
byte controlBit = _lfsr1.Step();
// 2. В зависимости от a(t) тактуется либо LFSR2, либо LFSR3
if (controlBit == 1)
{
_lfsr2.Step();
}
else
{
_lfsr3.Step();
}
// 3. Выход = XOR текущих выходов LFSR2 и LFSR3
return (byte)(_lfsr2.OutputBit ^ _lfsr3.OutputBit);
}
public byte[] GenerateBits(long count)
{
byte[] bits = new byte[count];
for (long i = 0; i < count; i++)
{
bits[i] = Step();
}
return bits;
}
}
/// <summary>
/// Пять основных статистических тестов на случайность
/// </summary>
public static class StatisticalTests
{
// 1. Частотный тест (одноразрядный)
public static double FrequencyTest(byte[] s)
{
long n0 = s.Count(b => b == 0);
long n1 = s.Count(b => b == 1);
long n = s.Length;
return Math.Pow(n0 - n1, 2) / n;
}
// 2. Тест на серии (двухразрядный)
public static double SerialTest(byte[] s)
{
long n = s.Length;
long n0 = s.Count(b => b == 0);
long n1 = s.Count(b => b == 1);
long n00 = 0, n01 = 0, n10 = 0, n11 = 0;
for (int i = 0; i < n - 1; i++)
{
if (s[i] == 0 && s[i + 1] == 0) n00++;
else if (s[i] == 0 && s[i + 1] == 1) n01++;
else if (s[i] == 1 && s[i + 1] == 0) n10++;
else if (s[i] == 1 && s[i + 1] == 1) n11++;
}
double p1 = (4.0 / (n - 1)) * (n00 * n00 + n01 * n01 + n10 * n10 + n11 * n11);
double p2 = (2.0 / n) * (n0 * n0 + n1 * n1);
return p1 - p2 + 1.0;
}
// 3. Обобщенный тест (m-разрядные блоки)
public static double GeneralizedTest(byte[] s, int m = 3)
{
int n = s.Length;
int k = n / m;
int numPatterns = 1 << m;
int[] counts = new int[numPatterns];
for (int i = 0; i < k; i++)
{
int pattern = 0;
for (int j = 0; j < m; j++)
{
pattern = (pattern << 1) | s[i * m + j];
}
counts[pattern]++;
}
double sumSquares = counts.Sum(c => (double)c * c);
return (numPatterns / (double)k) * sumSquares - k;
}
// 4. Тест на последовательности (длинные блоки/промежутки)
public static double RunsTest(byte[] s, int k = 3)
{
int n = s.Length;
int[] B = new int[k + 1]; // Блоки единиц
int[] G = new int[k + 1]; // Промежутки нулей
int currentType = s[0];
int currentLen = 1;
for (int i = 1; i < n; i++)
{
if (s[i] == currentType)
{
currentLen++;
}
else
{
int index = Math.Min(currentLen, k);
if (currentType == 1) B[index]++;
else G[index]++;
currentType = s[i];
currentLen = 1;
}
}
double x4 = 0;
for (int i = 1; i <= k; i++)
{
double e_i = (n - i + 3) / Math.Pow(2, i + 2);
x4 += Math.Pow(B[i] - e_i, 2) / e_i;
x4 += Math.Pow(G[i] - e_i, 2) / e_i;
}
return x4;
}
// 5. Автокорреляционный тест
public static double AutocorrelationTest(byte[] s, int d = 8)
{
int n = s.Length;
int A = 0;
for (int i = 0; i < n - d; i++)
{
A += (s[i] ^ s[i + d]);
}
return 2.0 * (A - (n - d) / 2.0) / Math.Sqrt(n - d);
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== Лабораторная работа №1 (Вариант 4: Бетта-Пайпера) ===");
// Для лабораторной работы берем представительную выборку (например, N = 100 000 битов)
int sampleSize = 100000;
BethPiperGenerator generator = new BethPiperGenerator();
Console.WriteLine($"Генерация последовательности из {sampleSize} битов...");
byte[] sequence = generator.GenerateBits(sampleSize);
// 1. Запись последовательности в файл
string filename = "output_sequence.txt";
File.WriteAllText(filename, string.Join("", sequence));
Console.WriteLine($"Последовательность успешно записана в файл: {filename}");
// 2. Расчет статистических тестов (alpha = 0.05)
Console.WriteLine("\n=== Результаты статистического тестирования ===");
double x1 = StatisticalTests.FrequencyTest(sequence);
Console.WriteLine($"1. Частотный тест: X1 = {x1:F4} (Порог = 3.8415) -> {(x1 < 3.8415 ? "ПРОЙДЕН" : "ПРОВАЛЕН")}");
double x2 = StatisticalTests.SerialTest(sequence);
Console.WriteLine($"2. Тест на серии: X2 = {x2:F4} (Порог = 5.9915) -> {(x2 < 5.9915 ? "ПРОЙДЕН" : "ПРОВАЛЕН")}");
double x3 = StatisticalTests.GeneralizedTest(sequence, m: 3);
Console.WriteLine($"3. Обобщенный тест (m=3): X3 = {x3:F4} (Порог = 14.0671) -> {(x3 < 14.0671 ? "ПРОЙДЕН" : "ПРОВАЛЕН")}");
double x4 = StatisticalTests.RunsTest(sequence, k: 3);
Console.WriteLine($"4. Тест на последовательности: X4 = {x4:F4} (Порог = 9.4877) -> {(x4 < 9.4877 ? "ПРОЙДЕН" : "ПРОВАЛЕН")}");
double x5 = StatisticalTests.AutocorrelationTest(sequence, d: 8);
Console.WriteLine($"5. Автокорреляционный тест (d=8): X5 = {Math.Abs(x5):F4} (Порог = 1.9600) -> {(Math.Abs(x5) < 1.9600 ? "ПРОЙДЕН" : "ПРОВАЛЕН")}");
}
}
}