2023-12-02:用go语言,如何求模立方根?

x^3=a mod p,

p是大于等于3的大质数,

a是1到p-1范围的整数常数,

x也是1到p-1范围的整数,求x。

p过大,x不能从1到p-1遍历。

答案2023-12-02:

灵捷3.5

大体步骤如下:

1.判断是否存在模立方根。有0,1,3个根这三种情况。

1.1.求p-1和3的最大公约数gcd(p-1,3)。最后结果要么是1,要么是3。如果是1,那肯定模立方根,但只有1个根。如果是3,进行下一步。

1.2.欧拉判别法。a**[(p-1)/3]==1 mod p。如果等于1,那就有3个根。如果不等于1,那就是0个根。

2.Peralta算法。求y。

2.1.当只有0个根时,直接返回。

2.2.当只有1个根时,a ^ ((p-1)/3) mod p就是答案。

2.3.当有3个根时,这个很难描述,具体见代码。

2.3.1.定义复数乘法和复数的快速幂。这虽然叫复数,但跟传统意义上的复数是不一样的。

2.3.2.确定一个常数r(r>=1并且r<p),使得 x ^ 3=r ^ 3 - a mod p 无根。

2.3.3.确定一个复数根,对这个复数根作复数的快速幂运算,指数是(p^2+p+1)/3,最终结果就是需要的根。

时间复杂度为 O((log p)^3)。

额外空间复杂度为 O(1)。

go完整代码如下:

package main

import (
"fmt"
"math/big"
) func main() {
if true { if false {
p := big.NewInt(0)
p.SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
for c := big.NewInt(20000); c.Cmp(big.NewInt(30000)) <= 0; c.Add(c, big.NewInt(1)) {
fmt.Println("c = ", c, "-------------")
r := ModCbrt(c, p)
fmt.Println("答案:", r)
for i := 0; i < len(r); i++ {
if big.NewInt(0).Exp(r[i], big.NewInt(3), p).Cmp(c) == 0 { } else {
fmt.Println("答案错误", r[i], ",c = ", big.NewInt(0).Exp(r[i], big.NewInt(3), p))
return
}
}
}
return
}
if true {
p := big.NewInt(0)
p.SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
for c := big.NewInt(20000); c.Cmp(big.NewInt(30000)) <= 0; c.Add(c, big.NewInt(1)) {
fmt.Println("c = ", c, "-------------")
r := ModCbrt(c, p)
fmt.Println("答案:", r)
for i := 0; i < len(r); i++ {
if big.NewInt(0).Exp(r[i], big.NewInt(3), p).Cmp(c) == 0 { } else {
fmt.Println("答案错误", r[i], ",c = ", big.NewInt(0).Exp(r[i], big.NewInt(3), p))
return
}
}
}
return
} if true {
p := big.NewInt(997)
for c := big.NewInt(1); c.Cmp(big.NewInt(0).Add(p, big.NewInt(-1))) <= 0; c.Add(c, big.NewInt(1)) {
fmt.Println("c = ", c, "-------------")
r := ModCbrt(c, p)
fmt.Println("答案:", r)
for i := 0; i < len(r); i++ {
if big.NewInt(0).Exp(r[i], big.NewInt(3), p).Cmp(c) == 0 { } else {
fmt.Println("答案错误", r[i], ",c = ", big.NewInt(0).Exp(r[i], big.NewInt(3), p))
return
}
}
}
}
return
}
fmt.Println("")
} // 求模立方根的个数0,1,3
func ModCbrtCount(c, p *big.Int) int {
t := big.NewInt(0)
t.Add(p, big.NewInt(-2))
t.Mod(t, big.NewInt(3))
if t.Cmp(big.NewInt(0)) == 0 {
return 1
}
t = big.NewInt(0).Add(p, big.NewInt(-1))
t.Div(t, big.NewInt(3))
if big.NewInt(0).Exp(c, t, p).Cmp(big.NewInt(1)) == 0 {
return 3
} else {
return 0
}
} // Peralta Method
func ModCbrt(a, p *big.Int) (ans []*big.Int) {
ans = make([]*big.Int, 0)
count := ModCbrtCount(a, p)
if count == 1 { //有1个解
t := big.NewInt(0).Lsh(p, 1)
t.Mod(t, p)
t = t.Add(t, big.NewInt(-1))
t.Mod(t, p)
t.Mul(t, big.NewInt(0).ModInverse(big.NewInt(3), p))
t.Mod(t, p)
ans = append(ans, big.NewInt(0).Exp(a, t, p))
} else if count == 3 { //有3个解,Peralta Method算法 w := big.NewInt(0)
p3 := big.NewInt(0).Add(p, big.NewInt(-1)) //(p-1)/3
p3.Mul(p3, big.NewInt(0).ModInverse(big.NewInt(3), p))
p3.Mod(p3, p)
for i := big.NewInt(1); i.Cmp(p) < 0; i.Add(i, big.NewInt(1)) {
w.Exp(i, p3, p)
if w.Cmp(big.NewInt(1)) != 0 {
break
}
}
var x *big.Int
key := big.NewInt(0)
for x = big.NewInt(1); x.Cmp(p) < 0; x.Add(x, big.NewInt(1)) {
key.Exp(x, big.NewInt(3), p) //key=x^3-a
key.Add(key, big.NewInt(0).Neg(a))
key.Mod(key, p)
if key.Cmp(big.NewInt(0)) != 0 && ModCbrtCount(key, p) == 0 {
break
}
}
r := Ring{x, big.NewInt(0).Add(p, big.NewInt(-1)), big.NewInt(0), key}
pp := big.NewInt(0).Mul(p, p) // pp = (p*p+p+1)/3,注意pp是不能 mod p的,有点反直觉
pp.Add(pp, p)
pp.Add(pp, big.NewInt(1))
pp.Div(pp, big.NewInt(3))
ansr := powerModI(r, pp, p)
ans0 := ansr.a
ans1 := big.NewInt(0)
ans1.Mul(ans0, w)
ans1.Mod(ans1, p)
ans2 := big.NewInt(0)
ans2.Mul(ans1, w)
ans2.Mod(ans2, p)
ans = append(ans, ans0, ans1, ans2)
}
return
} type Ring struct {
a *big.Int
b *big.Int
c *big.Int
w *big.Int
} // 复数乘法
func mulI(x Ring, y Ring, p *big.Int) Ring {
var res Ring
res.a = big.NewInt(0)
res.b = big.NewInt(0)
res.c = big.NewInt(0)
res.w = x.w
w := x.w a1 := big.NewInt(0)
a2 := big.NewInt(0)
a3 := big.NewInt(0)
a1.Mul(x.a, y.a) //x.a*y.a
a1.Mod(a1, p)
a2.Mul(x.b, y.c) //x.b*y.c*key
a2.Mod(a2, p)
a2.Mul(a2, w)
a2.Mod(a2, p)
a3.Mul(x.c, y.b) //x.c*y.b*key
a3.Mod(a3, p)
a3.Mul(a3, w)
a3.Mod(a3, p)
res.a.Add(a1, a2)
res.a.Mod(res.a, p)
res.a.Add(res.a, a3)
res.a.Mod(res.a, p) b1 := big.NewInt(0)
b2 := big.NewInt(0)
b3 := big.NewInt(0)
b1.Mul(x.a, y.b) //x.a*y.b
b1.Mod(b1, p)
b2.Mul(x.b, y.a) //x.b*y.a
b2.Mod(b2, p)
b3.Mul(x.c, y.c) //x.c*y.c*key
b3.Mod(b3, p)
b3.Mul(b3, w)
b3.Mod(b3, p)
res.b.Add(b1, b2)
res.b.Mod(res.b, p)
res.b.Add(res.b, b3)
res.b.Mod(res.b, p) c1 := big.NewInt(0)
c2 := big.NewInt(0)
c3 := big.NewInt(0)
c1.Mul(x.a, y.c) //x.a*y.c
c1.Mod(c1, p)
c2.Mul(x.b, y.b) //x.b*y.b
c2.Mod(c2, p)
c3.Mul(x.c, y.a) //x.c*y.a
c3.Mod(c3, p)
res.c.Add(c1, c2)
res.c.Mod(res.c, p)
res.c.Add(res.c, c3)
res.c.Mod(res.c, p) return res
} // 复数快速幂,注意b不能取模
func powerModI(a Ring, b, p *big.Int) Ring {
res := Ring{big.NewInt(1), big.NewInt(0), big.NewInt(0), a.w}
for b.Cmp(big.NewInt(0)) != 0 {
if big.NewInt(0).Mod(b, big.NewInt(2)).Cmp(big.NewInt(1)) == 0 {
res = mulI(res, a, p)
}
a = mulI(a, a, p)
b.Rsh(b, 1)
}
return res
}

2023-12-02:用go语言,如何求模立方根? x^3=a mod p, p是大于等于3的大质数, a是1到p-1范围的整数常数, x也是1到p-1范围的整数,求x。 p过大,x不能从1到p-1遍的更多相关文章

  1. n对mod求模整除时转化成mod的数学式

    n对mod求模,它的值在0到mod-1之间,如果要求模整除的时候转化成mod可以用下面的式子: n = (n - 1 % mod + mod) % mod +1 这里先减一,模上mod再加一,这样如果 ...

  2. LoadRunner 12.02 安装教程及中文语言包安装

    注意事项: 安装前,把所有的杀毒软件和防火墙关闭. 若以前安装过LoadRunner,则将其卸载. 安装路径不要带中文字符. LoadRunner 12已经不再支持xp系统,仅支持win7和win8系 ...

  3. HP LoadRunner 12.02 Tutorial T7177-88037教程独家中文版

    HP LoadRunner 12.02 Tutorial T7177-88037教程独家中文版 Tylan独家呕血翻译 转载请注明出自“天外归云”的博客园 Welcome to the LoadRun ...

  4. LoadRunner 12.02 安装以及汉化教程

    LoadRunner 12.02 安装 一.下载 首先下载Loadrunner12安装包. 下载后有四个安装包: HP_LoadRunner_12.02_Community_Edition_Addit ...

  5. 12天学好C语言——记录我的C语言学习之路(Day 11)

    12天学好C语言--记录我的C语言学习之路 Day 11: 因为指针部分比较的难,所以我们花费的时间也是最长的,希望大家耐的住性子,多多理解,多多打代码.好了,废话不多说,来看第11天的学习. //编 ...

  6. 12天学好C语言——记录我的C语言学习之路(Day 10)

    12天学好C语言--记录我的C语言学习之路 Day 10: 接着昨天的指针部分学习,有这么一个题目: //还是四个学生,四门成绩,只要有学生一门功课没及格就输出这个学生的所有成绩 /*//progra ...

  7. 12天学好C语言——记录我的C语言学习之路(Day 9)

    12天学好C语言--记录我的C语言学习之路 Day 9: 函数部分告一段落,但是我们并不是把函数完全放下,因为函数无处不在,我们今后的程序仍然会大量运用到函数 //转入指针部分的学习,了解指针是什么 ...

  8. 12天学好C语言——记录我的C语言学习之路(Day 8)

    12天学好C语言--记录我的C语言学习之路 Day 8: 从今天开始,我们获得了C语言中很有力的一个工具,那就是函数.函数的魅力不仅于此,一个程序到最后都是由众多函数组成的,我们一定要用好函数,用熟练 ...

  9. 12天学好C语言——记录我的C语言学习之路(Day 7)

    12天学好C语言--记录我的C语言学习之路 Day 7: 昨天进行了一天的数组学习,今天大家可以先写几个昨天的程序热热身,回顾回顾,然后今天第一个新程序也是关于数组的,比较难,准备好就开始啦! //输 ...

  10. 12天学好C语言——记录我的C语言学习之路(Day 6)

    12天学好C语言--记录我的C语言学习之路 Day 6: 今天,我们要开始学习数组了. //①数组部分,数组的大小不能够动态定义.如下: //int n;   scanf("%d,& ...

随机推荐

  1. vivo 容器集群监控系统优化之道

    作者:vivo 互联网容器团队 - Han Rucheng 本文介绍了vivo容器团队基于 Prometheus等云原生监控生态来构建的容器集群监控体系,在业务接入容器监控的过程中遇到的挑战.困难,并 ...

  2. [Lua] IT技术熟练度生成器 | 根据IT活动记录生成md表格 | 自创

    IT技术熟练度 v1.0 为衡量个人能力水平自创的一套评分机制,根据时间.代码行数.基础理论三个变量生成.最近在学lua,正好练下基本功.效果可见 个人介绍 | 代码统计 - 小能日记 - 博客园 ( ...

  3. AI测试,给出的答案还挺那么回事儿的~

    今天文心一言全民开放了,所有人都可以正常下载使用了,不用像之前一样排队等号了.之前内测阶段倒也体验过,技术人员总是喜欢尝鲜,第一时间拿到邀请码后就各种调戏了TA一番,那时觉得给出的答案总有些差强人意, ...

  4. 2023羊城杯RE部分

    vm_wo 代码copy下来调了一下 vm_body[0]=input[i] vm_body[1]=vm_body[0]>>1 v12=vm_body[0] vm_body[2]=v12& ...

  5. 拯救“消失的她”——双系统grub完美恢复方案

    双系统grub意外消失怎么办? 不用重装系统.不用去维修店.不会丢数据,教你一招,完美恢复grub! 背景 我的电脑是windows和linux双系统,启动项使用的grub.某天准备切换linux时突 ...

  6. 在 Net7.0环境下测试了 Assembly.Load、Assmebly.LoadFile和Assembly.LoadFrom的区别

    一.简介 很长时间没有关注一些C#技术细节了,主要在研究微服务.容器.云原生.编批等高大上的主题了,最近在写一些框架的时候,遇到了一些和在 Net Framework 框架下不一样的情况,当然了,我今 ...

  7. DHorse v1.4.0 发布,基于 k8s 的发布平台

    版本说明 新增特性 提供Fabric8客户端操作k8s(预览)的功能,可以通过指定-Dkubernetes-client=fabric8参数开启: Vue.React应用增加Pnpm.Yarn的构建方 ...

  8. MySQL 日志管理、备份与恢复

    MySQL 日志管理.备份与恢复 ---MySQL 日志管理--- MySQL 的日志默认保存位置为 /usr/local/mysql/data vim /etc/my.cnf [mysqld] ## ...

  9. Solution Set -「ARC 109」

    「ARC 109A」Hands Link. 讨论即可,除了煞笔出题人写了个死马的题面. #include<cstdio> #include<algorithm> using n ...

  10. tiptop查询通配符

    *:表示任何符合的字符,例:A*,表示要找出全部为 A 开头的资料. ?:表示任一符合的字符,例:A?,表示要找出第一码为 A,第二码为任何 字符,但总共只有二码之数据. 注:以上二功能仅可在文字字段 ...