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
相关推荐
晊晌_h3 小时前
嵌入式从0到精通——数据结构总结[特殊字符]
数据结构·算法·排序算法
geovindu4 小时前
CSharp: 万年历
开发语言·后端·c#·.net
我找到地球的支点啦4 小时前
Matlab系列(009) 一CRC循环冗余校验详解
开发语言·数据结构·算法·matlab·信息与通信
lisin-lee-cooper9 小时前
【leetcode658】有序数组找出k个最接近x的数
java·数据结构·算法
sunburn-10 小时前
Java堆(Heap)详解与实战教学
java·开发语言·数据结构·ide·算法
离陌在学C#11 小时前
C# 异步编程:从 async/await 到 Task 实战指南
开发语言·数据库·c#
哭泣方源炼蛊11 小时前
并查集进阶 P1(带权并查集,并查集分类)
数据结构·c++·算法·二进制·带权并查集
五_谷_丰_登12 小时前
平衡二叉树(AVL)讲解二
数据结构·c++
fpcc14 小时前
数据结构和算法—最短路径的算法
数据结构·算法
Mr. zhihao14 小时前
从数组到 B+ 树:一棵树的进化史
数据结构·innodb·b+树