30个整数映射到1个字节,查表法实现小范围内常数时间素性判定

想用查表法实现小范围内的常数时间的素性判定,怎样节省存储空间呢?自然是1个bit代表1个整数的素性,同时,偶数不用存,个位为5的整数不用存,只有个位为1、3、7、9的整数才可能是素数,也就是每20个整数中只有8个数可能是素数,正好1个字节。

但本文的标题是30个整数映射到1个字节,说明上面的方法还有优化的潜力。

让我们来考虑31~60这30个整数中最多有几个数可能是素数,所有偶数以及个位是5的数都是合数,此时剩余12个数待判定,所有是3的倍数的、个位非5的奇数也都是合数(33,39,51,57),此时正好剩下12-4=8个数待判定,正好1个字节。这个结论对形如30k+1,30(k+1)的区间都成立,其中k>0,这是因为30正好是2、3、5的最小公倍数。对于区间1,30,只有2、3、5这3个小素数是例外(按照上面的说法会被判定为合数),编程时可以单独处理,无伤大雅。

所以,对于任意正整数k,只有30k+(1,7,11,13,17,19,23,29)这8个奇数可能是素数,正好用1个字节存储。

下面是生成这个素性表的MATLAB代码,其中N=256,就是用256Byte存储了256×30=7680个整数的素性。N可以随意调大。

Matlab 复制代码
clc
N=256;
PrimalityTable=zeros(N,1,'uint8');
for k=1:N
    n=(k-1)*30;
    a=uint8(0);
    if isprime(n+1)
        a=bitor(a,uint8(128));
    end
    if isprime(n+7)
        a=bitor(a,uint8(64));
    end
    %-----------------------
    if isprime(n+11)
        a=bitor(a,uint8(32));
    end
    if isprime(n+13)
        a=bitor(a,uint8(16));
    end
    if isprime(n+17)
        a=bitor(a,uint8(8));
    end
    if isprime(n+19)
        a=bitor(a,uint8(4));
    end  
    %-----------------------
    if isprime(n+23)
        a=bitor(a,uint8(2));
    end
    if isprime(n+29)
        a=bitor(a,uint8(1));
    end
    PrimalityTable(k)=a;
    % dec2bin(a)
end

f = fopen('PrimalityTable.txt','w','n','UTF-8');

n_up=floor(N/16);
for n=1:n_up
    for k=1:16
        fprintf(f,"%3d, ",PrimalityTable((n-1)*16+k));
    end
    fprintf(f,"\r\n");
end
for k=1:(N-n_up*16)
    fprintf(f,"%3d, ",PrimalityTable(n_up*16+k));
end

%以下为正确性检验代码
Map0_29To2n = uint8([0, 128, 0, 0, 0, 0, 0, 64, 0, 0, 0, 32, 0, 16, ...
                     0, 0, 0, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1]);
for n=1:(N-1)*30
    k=floor(n/30);
    r=n-k*30;
    if xor(bitand(PrimalityTable(k+1), Map0_29To2n(r+1)), isprime(n))
        n
    end
end

primes(256)
length(primes(N*30))

数组Map0_29To2n的来历如下,本质就是把1,7,11,13,17,19,23,29这些可能为素数的位置挑选出来,

|----|-----|
| 0 | 0 |
| 1 | 128 |
| 2 | 0 |
| 3 | 0 |
| 4 | 0 |
| 5 | 0 |
| 6 | 0 |
| 7 | 64 |
| 8 | 0 |
| 9 | 0 |
| 10 | 0 |
| 11 | 32 |
| 12 | 0 |
| 13 | 16 |
| 14 | 0 |
| 15 | 0 |
| 16 | 0 |
| 17 | 8 |
| 18 | 0 |
| 19 | 4 |
| 20 | 0 |
| 21 | 0 |
| 22 | 0 |
| 23 | 2 |
| 24 | 0 |
| 25 | 0 |
| 26 | 0 |
| 27 | 0 |
| 28 | 0 |
| 29 | 1 |

下面是用C#实现的1~7680内全部整数的常数时间素性判定,超过7680的数,就可以使用试除法或者Miller-Rabin算法进行素性判定。

cs 复制代码
namespace CSharpIsPrime
{
    internal class Program
    {
        public readonly static byte[] Map0_29To2n = [0, 128, 0, 0, 0, 0, 0, 64, 0, 0, 0, 32, 0, 16, 
                                                     0, 0, 0, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1];
        public readonly static byte[] PrimalityTable = [
            127, 251, 247, 126, 109, 219, 188, 159, 171, 242, 120, 207,  87, 101, 183, 121,
            103,  48, 203, 203, 220, 187, 154, 165,  86, 230,  73, 189,  30, 120, 101, 106,
            106, 199, 181, 180, 123,  84,  50, 170, 155, 197,  15, 249, 192,  42, 133,  31,
            116, 191,  34, 151, 102, 111, 200,  92,  29,  50, 212,  92, 162, 136, 253,  42,
             49, 131,  94, 205,  19,  61,  49, 242, 132,  26, 142, 142, 217, 131, 232, 247,
             42, 105,  88,  16, 167, 193,  49,  98,  78, 223, 117, 166,  73, 241,  26, 225,
             75,  73,  27, 129, 166, 100, 199,   5, 136,  28, 227, 100,  60, 129, 215, 153,
            177, 138,  17, 124,  36, 207, 204, 178,  90, 209,  56, 229,  84,  45,  26,  50,
            114, 100, 111, 152,  65,  59, 193, 195,  52, 143,  28,  64, 173, 179, 179,  64,
             77,  82,  41,  48, 234,  50,  94,  12, 194, 208, 143, 211,  34,  54,  36,  31,
            152, 128, 169,  21,  58, 206,  87, 177,  36, 105, 212,  10, 101,  68, 120,  35,
            139,  18,  96,  43,  92, 244,  46,  57, 224,  86, 160,  17, 253,  22, 168, 116,
              6, 170, 199, 237, 138,  25,  16,  40,  97,  90,  85, 162, 178, 146,  14, 228,
             75, 201, 171,  83, 213,  64, 193, 134, 160,  36, 115, 225,  68,  67, 149, 181,
             24,  49, 178,  30, 139, 145, 104,  13, 234, 227,  70,  69,   3,  44,  36,  74,
            117,  90,   2,  76, 177, 132,  16, 194,  44, 109,  75, 109, 155, 152, 135,   6];

        static void Main(string[] args)
        {
            for (int i = 0; i < 256; i++)
            {
                if (IsPrime(i))
                {
                    Console.Write($"{i}, ");
                }
            }
            Console.WriteLine();

            int PrimeCount = 0;
            for (int i = 0; i < (PrimalityTable.Length * 30 + 1); i++)
            {
                if (IsPrime(i))
                {
                    PrimeCount++;
                }
            }
            Console.WriteLine($"PrimeCount: {PrimeCount}");
        }
        public static bool IsPrime(int n)
        {
            ArgumentOutOfRangeException.ThrowIfLessThan(n, 0, "n must be greater than or equal to 0.");
            bool isPrime = false;
            if (n < (PrimalityTable.Length * 30 + 1))
            {
                switch (n)
                {
                    case 0:
                    case 1:
                        return false;
                    case 2:
                    case 3:
                        return true;
                    case 4:
                        return false;
                    case 5:
                        return true;
                    default:
                        if (n % 2 == 0 || n % 3 == 0 || n % 5 == 0)
                        {
                            return false;
                        }
                        else
                        {
                            int quot = n / 30;
                            int rem = n - quot * 30;  // rem = n % 30
                            if ((PrimalityTable[quot] & Map0_29To2n[rem]) != 0)
                            {
                                return true;
                            }
                            else
                            {
                                return false;
                            }
                        }
                }
            }
            else
            {
                // Miller-Rabin primality test
            }
            return isPrime;
        }
    }
}

C#代码的输出结果如下,与MATLAB的验证代码输出结果一致。

cs 复制代码
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 
131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 
197, 199, 211, 223, 227, 229, 233, 239, 241, 251,

PrimeCount: 973
相关推荐
chingho16 小时前
GitHub上这10个C#项目,不仅实用,还怪好玩的
c#·github·.net·开源项目
心仪久了会沦陷17 小时前
C语言数据结构入门——队列补充:循环队列的数组实现
c语言·开发语言·数据结构·笔记
careathers18 小时前
【数据结构】二叉树拓展
数据结构·算法
半亩码田18 小时前
C#转Python第5.1篇:异步编程:asyncio vs async/await,语法相似但机制不同
开发语言·python·c#
m0_5474866618 小时前
《数据结构:基于C语言的描述》全套PPT课件2026
c语言·数据结构
LMT_Lanmantech18 小时前
MATLAB合规管理:如何避免侵权赔偿和审计风险
开发语言·matlab·信息可视化
阿华田51219 小时前
Java 类加载冲突实战:解决 is not an instance of
c#·linq
Evand J19 小时前
【PMSM例程6】永磁同步电机(PMSM)基于增广EKF的d-q轴电感分阶段辨识算法|电机参数估计与误差分析
算法·matlab·电机·ekf
垆边人似月.19 小时前
华为题:最多可以参加的会议数目
数据结构·c++·算法
Tongzhi202619 小时前
2026 年考勤行业趋势:考勤正在从“打卡工具“变成“人力成本中枢“
大数据·数据结构·数据仓库·科技·数据库开发