关于算法:手撸golang-基本数据结构与算法-素性测试费马测试

5次阅读

共计 3853 个字符,预计需要花费 10 分钟才能阅读完成。

缘起

最近浏览 << 我的第一本算法书 >>(【日】石田保辉;宫崎修一)
本系列笔记拟采纳 golang 练习之

素性测试, 费马测试

 素性测试是判断一个自然数是否为素数的测试。素数(prime number)就是只能被 1 和其本身整除,且大于 1 的自然数。目前在加密技术中被广泛应用的 RSA 算法就会用到大素数,因而“素性测试”在该算法中起到了重要的作用。费马测试被称为概率性素性测试,它判断的是“某个数是素数的概率大不大”。对于任意素数 p, 以及小于 p 的自然数 n, 
必然有: (n^p) % p = n,
这就是 "费马小定理"。依据是否满足费马小定理来判断一个数是否为素数的办法就是“费马测试”。摘自 << 我的第一本算法书 >>【日】石田保辉;宫崎修一 

  • 费马测试是素性测试的必要非充分条件
  • 因为素数在所有自然数中的占比是多数, 因而面对大数区间时, 使用费马测试能够大大减速素性测试的判断

疾速幂取余

  • 费马测试须要计算 n 的 p 次幂, n 是任意小于 p 的自然数, p 通常是大数, 因而计算结果十分微小, 很容易溢出
  • 可依据数学定理将大数合成为屡次小数的计算: 积的余 = 余的积取余, 即 (ab)%p = ((a%p)(b%p)) % p

设计

  • IPrimeTester: 素性测试器接口
  • tSimplePrimeTester: 循环整除法进行素性测试,. 实现 IPrimeTester 接口
  • tFermatTester: 费马测试器, 实现 IPrimeTester 接口

单元测试

fermat_test.go, 验证和比照循环整除法和费马测试的效率, 尤其是大于 2^32 以上的大整数

package others

import (
    "fmt"
    "learning/gooop/others/fermat_test"
    "testing"
    "time"
)

func Test_FermatTest(t *testing.T) {fnAssertTrue := func(b bool, msg string) {
        if !b {t.Fatal(msg)
        }
    }

    fnTestPrime := func(tester fermat_test.IPrimeTester) {primes := []int{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}
        set := make(map[int]bool, 0)
        for _,v := range primes {fnAssertTrue(tester.IsPrime(v), fmt.Sprintf("expecting %v is prime", v))
            set[v] = true
        }
        for i := 0;i < 100;i++ {if _,ok := set[i];!ok {fnAssertTrue(!tester.IsPrime(i), fmt.Sprintf("expecting %v is not prime", i))
            }
        }
    }


    s := fermat_test.SimplePrimeTester
    f := fermat_test.FermatPrimeTester
    t.Log("testing SimplePrimeTester range 0-100")
    fnTestPrime(s)
    t.Log("testing FermatPrimeTester range 0-100")
    fnTestPrime(f)

    t.Log("testing range 100-100_000")
    for i := 100;i < 100_000;i++ {fnAssertTrue(s.IsPrime(i) == f.IsPrime(i), fmt.Sprintf("failed testing %v", i))
    }

    fnTestCost := func(tester fermat_test.IPrimeTester, from, to int) {t0 := time.Now().UnixNano()
        fnAssertTrue(tester.IsPrime(2147483647), "expecting 2147483647 is prime")
        for i := from;i <= to;i++ {tester.IsPrime(i)
        }
        cost := (time.Now().UnixNano() - t0) / 1000_000
        t.Logf("cost = %v ms", cost)
    }

    t.Log("testing SimplePrimeTester range 0xffff_fffff000 to 0xffff_ffffffff")
    fnTestCost(s, 0xffff_fffff000, 0xffff_ffffffff)

    t.Log("testing FermatPrimeTester range 0xffff_fffff000 to 0xffff_ffffffff")
    fnTestCost(f, 0xffff_fffff000, 0xffff_ffffffff)
    t.Log(f)
}

测试输入

因为质数在自然数中的占比是多数, 因而使用费马测试进行过滤, 能大大减速大数区间的质数筛选

$ go test -v fermat_test.go 
=== RUN   Test_FermatTest
    fermat_test.go:34: testing SimplePrimeTester range 0-100
    fermat_test.go:36: testing FermatPrimeTester range 0-100
    fermat_test.go:39: testing range 100-100_000
    fermat_test.go:54: testing SimplePrimeTester range 0xffff_fffff000 to 0xffff_ffffffff
    fermat_test.go:51: cost = 23752 ms
    fermat_test.go:57: testing FermatPrimeTester range 0xffff_fffff000 to 0xffff_ffffffff
    fermat_test.go:51: cost = 6 ms
    fermat_test.go:59: Fermat(fail=94464, pass=9629)
--- PASS: Test_FermatTest (23.88s)
PASS
ok      command-line-arguments  23.885s

IPrimeTester.go

素性测试器接口

package fermat_test

type IPrimeTester interface {IsPrime(int) bool
}

tSimplePrimeTester.go

循环整除法进行素性测试,. 实现 IPrimeTester 接口

package fermat_test

import "math"

type tSimplePrimeTester struct {
}

func newSimplePrimeTester() IPrimeTester {return &tSimplePrimeTester{}
}

func (me *tSimplePrimeTester) IsPrime(x int) bool {
    if x <= 1 {return false}

    if x <= 3 {return true}

    for i, q := 2,int(math.Floor(math.Sqrt(float64(x))));i <= q;i++ {
        if x % i == 0 {return false}
    }

    return true
}

var SimplePrimeTester = newSimplePrimeTester()

tFermatTester.go

费马测试器, 实现 IPrimeTester 接口

package fermat_test

import "fmt"

type tFermatTester struct {
    iFailedCount int
    iPassedCount int
}

func newFermatTester() IPrimeTester {return &tFermatTester{}
}

var arrSampleNum = []int{ 1,2,3,4,5,6,7,8,9}
func (me *tFermatTester) IsPrime(x int) bool {
    if x <= 1 {return false}

    if x <= 3 {return true}

    for i,size := 0, len(arrSampleNum);i < size;i++ {x1 := x * arrSampleNum[i] / 10
        if x1 <= 1 {break}

        if !me.fermatTest(x1, x) {
            me.iFailedCount++
            return false
        }
    }

    me.iPassedCount++
    return SimplePrimeTester.IsPrime(x)
}

// 费马测试
func (me *tFermatTester) fermatTest(n int, p int) bool {
    // n^p % p == n ?
    return n == fastMod(n, p, p)
}

// 疾速幂取余
func fastMod(n int, p int, mod int) int {
    sum := 1
    for p > 0 {if (p & 1) > 0 {sum = (sum * n) % mod
            p--

        } else {
            p /= 2
            n = (n * n) % mod
        }
    }
    return sum
}

func (me *tFermatTester) String() string {return fmt.Sprintf("Fermat(fail=%v, pass=%v)", me.iFailedCount, me.iPassedCount)
}

var FermatPrimeTester = newFermatTester()

(end)

正文完
 0