You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Enigma模拟器的C#电信号模拟代码?

如何优化C#中大量转子的Enigma模拟代码?

首先得说,你当前的代码核心问题在于每处理一个字节就要遍历所有10万个转子,这直接导致时间复杂度变成O(n*m)(n是数据长度,m是转子数),这种量级的循环在C#里肯定会慢得离谱。下面给你几个针对性的优化方案,都是基于C#环境的,不用转C/C++:

1. 用Unsafe指针消除数组边界检查开销

C#的普通数组索引器默认会做边界检查,这在循环次数达到1e5级别的时候,累积的开销会非常大。改用unsafe指针直接访问数组,可以彻底去掉这个检查,性能提升会很明显。

比如把你的代码改成这样:

unsafe void Encrypt(byte[] data, byte[][] scramblers, byte[] key)
{
    fixed (byte* pData = data)
    fixed (byte* pKey = key)
    {
        // 先把所有转子的指针存到一个数组里,避免每次循环都取数组地址
        byte** pScramblers = stackalloc byte*[scramblers.Length];
        for (int j = 0; j < scramblers.Length; j++)
        {
            fixed (byte* pScrambler = scramblers[j])
            {
                pScramblers[j] = pScrambler;
            }
        }

        for (int i = 0; i < data.Length; i++)
        {
            byte val = pData[i];
            // 遍历转子加密
            for (int j = 0; j < scramblers.Length; j++)
            {
                val = pScramblers[j][(byte)(val + pKey[j])];
            }
            pData[i] = val;

            // 优化旋转逻辑:找到第一个不溢出的转子就停止
            int jRot = 0;
            while (jRot < key.Length)
            {
                pKey[jRot]++;
                if (pKey[jRot] != 0) break;
                jRot++;
            }
        }
    }
}

这里用stackalloc存储转子的指针,避免了每次循环都重新获取数组地址的开销,同时指针访问直接跳过了边界检查,能大幅降低循环的时间成本。

2. 预计算复合置换表(最有效的优化)

每个转子本质是一个字节置换函数,多个转子串联其实就是这些置换的复合(比如转子A是f(x),转子B是g(x),串联后就是g(f(x)))。如果我们能把所有转子的置换预先合并成一个单一的256元素数组(因为输入是byte,只有256种可能),那每处理一个字节只需要一次数组查找,时间复杂度直接从O(n*m)降到O(n + m)!

不过这里要注意你的转子旋转逻辑是进位式的:第一个转子每处理一个字节加1,溢出到0时第二个转子加1,以此类推。针对这个逻辑,我们可以批量处理数据:

  • 比如每处理256个字节,第一个转子会刚好溢出一圈(回到初始值),同时第二个转子加1。我们可以预先计算每一轮(256个字节)对应的复合置换表。
  • 预计算初始状态的复合表currentMap[256],其中currentMap[x]表示输入x经过所有转子后的结果。
  • 处理256个字节,每个字节直接用currentMap[data[i]]得到加密结果。
  • 然后更新转子状态(第二个转子加1),重新计算新的currentMap,再处理下一批256个字节。

代码大概是这样的:

void PrecomputeMap(byte[][] scramblers, byte[] key, byte[] outMap)
{
    for (int x = 0; x < 256; x++)
    {
        byte val = (byte)x;
        for (int j = 0; j < scramblers.Length; j++)
        {
            val = scramblers[j][(byte)(val + key[j])];
        }
        outMap[x] = val;
    }
}

void BatchEncrypt(byte[] data, byte[][] scramblers, byte[] key)
{
    byte[] currentMap = new byte[256];
    int batchSize = 256;
    int batches = data.Length / batchSize;
    int remaining = data.Length % batchSize;

    PrecomputeMap(scramblers, key, currentMap);

    // 处理完整批次
    for (int b = 0; b < batches; b++)
    {
        int start = b * batchSize;
        for (int i = 0; i < batchSize; i++)
        {
            data[start + i] = currentMap[data[start + i]];
        }

        // 旋转256次:第一个转子回到初始值,第二个转子加1(进位)
        key[1]++;
        if (key[1] == 0)
        {
            // 处理更高位的进位,这里可以递归或者循环处理
            int j = 2;
            while (j < key.Length && key[j-1] == 0)
            {
                key[j]++;
                j++;
            }
        }
        PrecomputeMap(scramblers, key, currentMap);
    }

    // 处理剩余字节
    for (int i = batches * batchSize; i < data.Length; i++)
    {
        byte val = data[i];
        for (int j = 0; j < scramblers.Length; j++)
        {
            val = scramblers[j][(byte)(val + key[j])];
        }
        data[i] = val;

        // 旋转一次
        int jRot = 0;
        while (jRot < key.Length)
        {
            key[jRot]++;
            if (key[jRot] != 0) break;
            jRot++;
        }
    }
}

这个优化的效果是爆炸级的:比如处理1e6个字节,原来需要1e6*1e5=1e11次操作,现在只需要(1e6/256)*1e5 +1e6 ≈ 3.9e7次操作,速度提升几千倍都有可能。

3. 其他小优化

  • 用Span替代普通数组:Span在release模式下会自动优化边界检查,性能接近unsafe指针,但不需要写unsafe代码。比如把scramblers改成Span<byte>[],访问时用scramblers[j][index]。
  • 减少内存分配:如果你的scramblers数组是固定的,尽量声明为readonly,让JIT编译器做更多优化。
  • 避免不必要的类型转换:原代码里的(byte)(data[i] + key[j])已经是byte类型,不需要额外转换,但要确保加法溢出是你想要的(符合Enigma的循环逻辑)。

最后要提醒的是,你的原代码里没有对(data[i] + key[j])取模,但C#里byte加法会自动溢出模256,这刚好符合Enigma的循环移位逻辑,是正确的。

内容的提问来源于stack exchange,提问作者daralim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:02:39