数字迷雾:在像素中寻找线索
(LSB隐写)
比赛中我们尝试了随波逐流工具简单得到了flag。
不过可以使用脚本,将像红色的像素点提取出来,然后将提取出的二进制数据转化为字符串。
虽然有点问题但有了flag
from PIL import Image
def LSB_image():
img = Image.open(r"C:\Users\HP\Desktop\Dkdun.png")
binary_data = ""
for y in range(img.size[1]):
for x in range(img.size[0]):
r, g, b = img.getpixel((x, y))
binary_data += bin(r)[-1] # 获取红色通道的LSB
return binary_data
def binary_to_string(binary_data):
n = 8
return ''.join(chr(int(binary_data[i:i+n], 2)) for i in range(0, len(binary_data), n))#使用列表推导式将每8位二进制数转换为对应的ASCII字符,并使用join方法将所有字符连接成一个字符串。
binary_data = LSB_image()
flag = binary_to_string(binary_data)
print(flag)
1z_php
过滤了一些危险的函数,使用其他的方法去读取它。首先查看它的根目录,发现lf14g
利用sort来读取flag
Crypto
小蓝鲨的密码
zip解压密码为bluesharkinfo
得到了密码本,txt里面是AES加密的内容,需要密钥的支持。
经过测试,密钥为isctf2024,
居然没有做出来,天塌了。
小蓝鲨的数学题
根据提示模数为2**512,这是一题离散数学的题目。之前没有接触过太多的离散数学。
这是个简单的离散数学的题目:离散对数求解
离散对数是数论中的一个概念,三个整数 aa、bb 和 mm,其中 aa 和 mm 互质,离散对数问题是要找到一个整数 xx,使得:
ax≡b(modm)ax≡b(modm)
在这个问题中,a=2512a=2512,b=cb=c,mm 是一个大整数。我们需要找到 xx,使得:
(2512)x≡c(modm)(2512)x≡c(modm)
from sympy import discrete_log
from Crypto.Util.number import long_to_bytes
m = 5321153468370294351697008906248782883193902636120413346203705810525086437271585682015110123362488732193020749380395419994982400888011862076022065339666193
c = 7383779796712259466884236308066760158536557371789388054326630574611014773044467468610300619865230550443643660647968413988480055366698747395046400909922513
flag = discrete_log(2**512,c,m)
print(long_to_bytes(flag))
ChaCha20-Poly1305
看到key?.txt,看起来像base,尝试一下,得到了base92解密为正确。
分析一下脚本:
使用 Python 的**Crypto.Cipher
模块中的 ChaCha20_Poly1305 算法进行文件加密** 我们可以知道ChaCha20_Poly1305 是一个对称加密算法 ,它结合了ChaCha20 流密码和 Poly1305 认证加密算法(AEAD,认证加密与解密),被谷歌选择后开始兴盛。
基本原理:
ChaCha是基于salsa密码的改良,20代表有20轮的加密计算
它还是属于一种流密码,因此以字节为单位进行加密,安全形是依赖于密钥生成器,加密过程是将密钥流与明文逐字节异或得到密文,解密是将密文与密钥流进行一次异或运算得到明文。
它的主要难点在于:ChaCha20中与明文进行异或的数据是如何产生的
用户之间会约定一些元数据,例如称为KEY_INIT,它经过某种运算,得到了另一个KEY_1,然后KEY_1与明文的第一个分组进行异或;接下来KEY_INIT经过某种运算,得到另一个KEY_2,它与明文的第二个分组进行异或得到密文的第二个分组,以此类推,每个
在chacha20里面KEY_INIT和KEY_n的长度都为64个字节,
那么我们排成矩阵,内容为:
cccccccc cccccccc cccccccc cccccccc
kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk
bbbbbbbb nnnnnnnn nnnnnnnn nnnnnnnn
其中c表示constant的4个比特,k表示key的4个bit,b表示block counter 的4个bit,n代表nonce的4个bit
chacha20 里面包含着两种形式:列运算和对角线运算
总体来说:
在 chacha20中,明文数据会被划分为若干个 64字节的分组,然后通过 chacha20算法计算出对应的 KEY_n(上面演示的矩阵),并让明文分组与 KEY_n相异或,得到对应的密文分组。
其中最重要的一点是,在每处理完一个分组后,矩阵 M中的 block counter部分需要自增 1,其他三个部分( constant, key,nonce)保持不变,这样就确保了对于不同的明文分组,对应的 KEY_n是不同的。若最后一个明文分组不足 64字节,则只异或 KEY_n中对应的字节。因此,即使明文分组不足 64字节,也不影响计算,但对应的 KEY_n的所有字节(矩阵 的所有元素)需要被全部计算出来。
Poly1305(MAC)
Poly1305消息认证码的输入为32字节(256bit)的密钥和任意长度的消息比特流,经过一系列计算生成16字节(128bit)的摘要。
密钥处理
首先将32字节的密钥分成16字节的两组,前16字节称作"r",后16字节称作"s",申请两个大小为16的数组r[]和s[]。
对于s,将其以字节为单位,倒序排列。
对于r, 将r[3],r[7],r[11],r[15]前4位清零(使其小于16),r[4],r[8],r[12]最后两位清零(能够被4整除),完成清零操作后,类按字节倒序排列。最后得到s和r.
加密函数
加密之前在系统中分配一个寄存器作为累加器ACC(程序中以数组表示),ACC初始值为0。明文划分为16字节一组,按照铭文长度s,划分为s/16组,进行s/16次加密。最后与s相加得到16字节明文摘要。
利用密钥、密文、认证标签(Tag)和一次性数字(Nonce)来解密被 ChaCha20_Poly1305 算法加密的flag
from Crypto.Cipher import ChaCha20_Poly1305
import binascii
# 已知的密钥、密文、认证标签和 nonce
key_hex = '173974535637a5ef30a116b03d00bd2fe751951ca3eaa62daec2b8f5ca5b6135'
ct_hex = '20408b9fc498063ad53a4abb53633a6a15df0ddaf173012d620fa33001794dbb8c038920273464e13170e26d08923aeb'
tag_hex = '70ffcc508bf4519e7616f602123c307b'
nonce_hex = 'd8ebeedec812a6d71240cc50'
# 将十六进制字符串转换为字节
key = binascii.unhexlify(key_hex)
ct = binascii.unhexlify(ct_hex)
tag = binascii.unhexlify(tag_hex)
nonce = binascii.unhexlify(nonce_hex)
# 创建解密对象
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
# 尝试解密并验证
try:
plaintext = cipher.decrypt_and_verify(ct, tag)
print("Decrypted Flag:", plaintext)
except ValueError as e:
print("Error:", e)
蓝鲨的费马
根据该代码可以得到了关键一点,
根据2023 数据安全产业人才能力挑战赛 --- math_exam wp的变种 ,而且**(p^q%n+q^p%n)%n=p+q,**
那么关键的代码就变成了这个样子:
leak=d+p+q
接下来就很简单了,
具体的代码:
import sympy
from Crypto.Util.number import *
from Crypto.Util.number import long_to_bytes
c= 8989289659072309605793417141528767265266446236550650613514493589798432446586991233583435051268377555448062724563967695425657559568596372723980081067589103919296476501677424322525079257328042851349095575718347302884996529329066703597604694781627113384086536158793653551546025090807063130353950841148535682974762381044510423210397947080397718080033363000599995100765708244828566873128882878164321817156170983773105693537799111546309755235573342169431295776881832991533489235535981382958295960435126843833532716436804949502318851112378495533302256759494573250596802016112398817816155228378089079806308296705261876583997
n= 13424018200035368603483071894166480724482952594135293395398366121467209427078817227870501294732149372214083432516059795712917132804111155585926502759533393295089100965059106772393520277313184519450478832376508528256865861027444446718552169503579478134286009893965458507369983396982525906466073384013443851551139147777507283791250268462136554061959016630318688169168797939873600493494258467352326974238472394214986505312411729432927489878418792288365594455065912126527908319239444514857325441614280498882524432151918146061570116187524918358453036228204087993064505391742062288050068745930452767100091519798860487150247
leak= 9192002086528025412361053058922669469031188193149143635074798633855112230489479254740324032262690315813650428270911079121913869290893574897752990491429582640499542165616254566396564016734157323265631446079744216458719690853526969359930225042993006404843355356540487296896949431969541367144841985153231095140361069256753593550199420993461786814074270171257117410848796614931926182811404655619662690700351986753661502438299236428991412206196135090756862851230228396476709412020941670878645924203989895008014836619321109848938770269989596541278600166088022166386213646074764712810133558692545401032391239330088256431881
m=pow(c,leak-n-1,n)
flag= long_to_bytes(m)
print(flag)
但是观察别人wp学到了新的方法,主要利用侧信道攻击,手推了下。
主要的代码如下:
得到p,q之后就常规的解法了。
l = e*leak % n
for t in range(0x10001):
if (l - 1 - t) % (e - t) == 0:
p_plus_q = (l - 1 - t) // (e - t)
delta = p_plus_q * p_plus_q - 4 * n
if delta <= 0 :
continue
squre_delta = iroot(delta,2)
if squre_delta[1]:
p = (p_plus_q + squre_delta[0])//2
q = (p_plus_q - squre_delta[0])//2
print(f"{t=}")
break
ezmath
题目:
import random
import base64
from hashlib import md5
from secret import flag
from libnum import s2n
from Crypto.Cipher import AES
INF = 0xff
bigINF = 0xffffffff
# ---------------------error------------------------------
def sumFunc(func):
def wapper(*args,start = 1,end=INF):
sums = 0
for i in trange(start, 0xffff * end):
sums += function(*args,j/0xf) * (1/0xf)
return sums
return wapper
def limitFunc(func):
def wapper(*args, approch = bigINF, pos = "+"):
o = 1/bigINF
return function(args, eval(f"{approch} {pos} {o}"))
return wapper
# -------------------enderror-----------------------------
def pad(data):
data=data.encode('utf8')
while len(data) % 16 !=0:
data+=b'\x00'
return data
def encode(key,m):
mode=AES.MODE_ECB
aes=AES.new(pad(key),mode)
en_m=aes.encrypt(pad(m))
en_m=base64.encodebytes(en_m)
en_m=en_m.decode('utf8')
return en_m
def enc(msg, key):
random.seed(key)
new_key = md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest()
return encode(new_key, msg)
@sumFunc
def gamma(x,t):
data = pow(t,x-1) * pow(magicNumber,-t)
return data
@sumFunc
def common(t):
data = pow(magicNumber,-pow(t,2))
return data
@limitFunc
def getMagicNumber(t):
data = pow(1+1/t,t)
return data
magicNumber = getMagicNumber()
encKey1 = str(gamma(3/2))[2:6]
encKey2 = str(common())[2:6]
assert encKey1 == encKey2
key = int(str(gamma(5/2))[2:])
print(enc(flag, key))
# n2SQK64JMsXstCtZurBiz81pMr3ZmgMjhuyL67hssm3shqJGYGfS/mWubINeE5HZ
这个题目是一个修改补充错误的代码的脚本,仔细分析一下。
INF,bigINF 分别代表着一个无穷大(0xff)和一个较大的无穷大(0xffffff),有两个装饰器:sumFunc对传入的函数进行求和,但是这个出现了错误,没有定义function和trange函数,而且循环变量应该是i而不是j;而另一个limitFunc对传入的函数进行限制,也没有定义function函数,并且未将args解引用。
主要逻辑:
计算magicNumber,然后使用它来生成两个加密密钥encKey1和encKey2,并断言它们相等,计算最终的加密密钥key,并使用它来加密flag。
修复后应该是这样
def sumFunc(func):
def wapper(*args, start=1, end=INF):
sums = 0
for i in range(start, 0xffff * end):
sums += func(*args, i/0xf) * (1/0xf)
return sums
return wapper
def limitFunc(func):
def wapper(*args, approch=bigINF, pos="+"):
o = 1/bigINF
return func(*args, eval(f"{approch} {pos} {o}"))
return wapper
修改它之后能够跑脚本
这里补充一下装饰器的的知识,
定义: 装饰器本质上是一个高阶函数,接受一个函数作为参数并返回一个新的修改过的函数。
允许我们不直接修改被装饰函数的代码的情况下添加额外的功能,比如日志记录、权限验证登。
一般通过@符号和一个包裹的函数来实现。
举个例子:
def my_secert(func):
def warpper():
print("Good good study,day day up!")
func()
print("Success!")
@my_secert
def haha_whee():
print("haha!")
上面的例子中装饰器my_secert和一个被装饰函数haha_whee().通过使用@my_secert语法糖,pyhton会自动调用my_secert(haha_whee)并将返回包裹函数分配给原始函数名称haha_whee.当我们调用haha_whee()时,实际上是调用装饰器返回的包裹函数。
而题目中的代码运用了两个装饰器,
umFunc
装饰器的作用:将一个函数转换为一个可以进行数值积分的函数。这里的积分是通过求和的方式来近似计算的。
sumFunc 接受一个函数 func 作为参数,定义了一个内部函数 wapper,这个函数接受任意数量的位置参数 *args,以及两个可选参数 start 和 end。start 默认为1,end 默认为 INF(这里 INF 应该是一个非常大的数,代表无穷大)。在 wapper 函数内部,使用了一个 for 循环来模拟积分的过程。循环从 start 开始,到 0xffff * end 结束(这里 0xffff 是16位十六进制数的最大值,即65535,用于控制步长)。
每次循环中,调用原始函数 func,并传入 *args 和当前的 i/0xf 作为参数,然后将结果乘以 1/0xf(这里 0xf 是十六进制的15,用于模拟步长)并累加到 sums 变量中。最后返回累加的结果 sums,这个结果就是对函数 func 在 start 到 end 区间的数值积分。
limitFunc 装饰器作用:计算函数的极限
limitFunc 接受一个函数 func 作为参数。定义了一个内部函数 wapper,这个函数接受任意数量的位置参数 *args,以及两个可选参数 approch 和 pos。approch 默认为 bigINF(这里 bigINF 应该是一个表示非常大数值的变量,用于模拟极限中的无穷大),pos 默认为 "+"。
而在 wapper 函数内部,使用 eval 函数来计算 approch 和一个非常小的数 1/bigINF 的运算结果。这里的 pos 参数决定了是加还是减这个小数。最后返回原始函数 func 调用的结果,这个结果就是函数在 approch 处的极限值。
解题的思路:这就要考你的高数了,
上面提到的装饰器,一个对函数的输出在一定的区间内进行数值积分,一个计算在另一个点的极限。
gamma(x,t):计算pow(t,x-1) * pow(magicNumber,-t)
getMagicNumber(t):计算极限pow(1+1/t,t)
magicNumber其实就是e
我们需要计算出magicNumber
的值。由于getMagicNumber(t)
是一个极限函数,可以通过让t
趋向于无穷大来计算这个极限值。然后,使用magicNumber
值来计算encKey1
和encKey2
,确保它们相等。接着,计算key
的值,它是gamma(5/2)
函数返回值的一部分,最后,使用计算出的key
值来解密。
解下来就来求key了,在进行AES-ECB解密就可以了得到flag了。
解密的脚本:
from math import sqrt,pi
from hashlib import md5
import random
key = int(str(3*sqrt(pi)/4)[2:])
print(key)
random.seed(key)
print(md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest())
import random
import base64
from hashlib import md5
from Crypto.Cipher import AES
def pad(data):
data = data.encode('utf8')
while len(data) % 16 != 0:
data += b'\x00'
return data
def decode(key, en_m):
mode = AES.MODE_ECB
aes = AES.new(pad(key), mode)
en_m = base64.decodebytes(en_m.encode('utf8'))
de_m = aes.decrypt(en_m)
return de_m.decode('utf8').rstrip('\x00')
def dec(encrypted_msg, key):
random.seed(key)
new_key = md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest()
return decode(new_key, encrypted_msg)
key = 329340388179137
encrypted_msg = "n2SQK64JMsXstCtZurBiz81pMr3ZmgMjhuyL67hssm3shqJGYGfS/mWubINeE5HZ"
flag = dec(encrypted_msg, key)
print(flag)
小蓝鲨的方程
题目:
我们可以看到n在构造的时候,q变成了q^4
利用p1=p^oder+a来求出a,接下来要求出p,q(这里稍微难一点)
利用 p1
和 p
之间的关系来缩小搜索范围。由于 p1
是 p
的四次方加一个较小的数,所以 p1
的四次方根将非常接近 p
。通过从这个近似值中减去一个小整数,我们可以快速检查是否能得到一个素数,这个素数就是 p
。
这种方法的效率取决于 a
的大小和 p
的四次方根的精度。如果 a
相对于 p
的四次方非常小,那么这种方法更有可能成功。此外,这种方法的成功也依赖于 p
和 q
的具体值,以及 p
的四次方根的整数部分是否接近 p
。
解题的脚本:
from libnum import n2s,s2n
from gmpy2 import *
from tqdm import trange
def crack(n, p1):
for i in trange(10000):
test = iroot(p1, 4)[0] - i
if not is_prime(test):
continue
else:
q = iroot(n // test, 4)
if q[1]:
q = q[0]
print()
print("found!")
return test,q
n = 1076246859437269645898003764327104347852443049519429833372038915264009774423737482018987571807662568251485615769880354898666799006772572239466617428164721157850526408878346223839884319846641438292436373441749602341461361190584638190903978829024853974880636148520803145113551453821058269641304504880310836801494499720662704717315748614372503735165114899680682056477494953525794354656896362929510309669119173103242509398650608116835276076364248473952717811633756784397347121601006659623317417388283638159905288128181587304367489096254611610975352096229116491567502061775862811850081040850421151385474249060884479729988512713640536139010928836126719149031115182144744359297169350288886555784650111
p1 = 145356063641618996012874664536921616978986640263438210169671010403677822239343590475177543891188656103067696467174379510912427160232486984044862545338401652910975162942038201716552753723984593267892098222213049269335313670049037479410635628460505327693176152061750827570561482918795206276991967169087371403553
c1 = 671390498592586008552998377599101093977542184109077889081448730480869018650843045119891777468161631085086340705902115332025675787789530562679603254577287153918966364523848382506106179394235772395029788721306186952016420794804145631124905952103136061076643266886961178241381892015555099638200222249447194504082451341122502519637821695210573997670753981061458264118355417889153180841281073262935937836447460470926729282834006229571453935760593644658459098721652426154970766417292435960463905367868753821950303919781798234432998272038029063155193184039985018137026245365188171178677898869374676546799536208952198558258306460302868688355653022725288744014143221560882404431652751343944983442109327
c = 8641190030376811670503537177719719233418166235794962118828671236836174132083208517733734760455990850156371205118391537919769888760384574011411232571257192285256730733174399297826587479261381970232162702657952399683882650083181048279650913795429823628186888540572704055008102853692060360140858142686334722286525699998854566609078547487420929457446776757558492454916447188774943818970599916514467335772992690805247630814156710861067503956707301402347944233660194395192354000788262111000900574820275786269075882923600474781645848712157460135387134196156906258218217831988828360827613420801773911833194097791649069743116686685667300622630909231822986237104627385544169938138006242341269672868611269202418482629393372933567053272565557137741441902377611003983050084491513897727856173625922194300103448148829004025229567101761111396110940066254801762424343522707712480796358754008120503317686600144600226149617189681233392693738216138797012278242152852923361635415564580582002132107424154426980566696622448291815571736676562214017436
e = 0x10001
p,q = crack(n, p1)
print(p, q)
phi = (p-1)*(q**4 - q**3)
d = invert(e, phi)
s = pow(c1, d, n)
m = (c - 1) // (s**3)
print(m)
print(n2s(int(m)))
这个题目和国赛的一个题目的思想比较相近,我们也来看看吧。
2024国赛------rasnd
很明显我们需要分成了两段进行解密
第一部分
实现的代码:
from tqdm import trange
from Crypto.Util.number import *
from gmpy2 import *
n=18088011671538976982165525440386623289385114080576725768019061415671826851943445221226512589098669346404026374951858999387217508024789211498259452109214556714912857033124082966625646395283686312524015320926512455915546499413478756620357509821566076816579297882375786426816605611526775168996549051600931509019387185312619492222782269305011713051789911005317468583129891804202627825567966747213383425120088700132546120060985737910313952154697082271457880737295887007466461730433266268402482331178579951103721119735101467400195497116119485338923706700491486973150788317315356357101151829456342562867587099598945192815263
c=9672261292049179510539936121485683732050798623479355794472893221642511300800280335280454378943002919160802677245360275424484528013159261954493742998677309529673790654057091714075262162318494670714730092015059383281995469507344322339633183388332778604852910046402244856048524492616127009392735657588616348180165737939024483272404465691736500951998475167207424220354207033328796782335018476327594119671551311477701303163670617853496320673657883624554669421593020604921475875559320715926280873207029420395750055202639374682706327888026109861279966152066895569848791223114397562133543690771564722708438398302054378748382
hint1=1876377888814200677442129576675996706468631990804911325305925446297494237080972549459539078790790063918048118238573069981792229335343412599922437368079227142591323977848118125493649176850872826534420257631894221784255713060216558942913054972531098351649650098921170981304230776828706602102714925788415307347441588418154129396919337838110092813
hint2=4577144295703606484123914611409444377581187954194894627593999949721725631702229741058762926738731162033453968685003890648825426935166041938739780782092132921278035040699628683026895248136976510810097939718444896419804529003179001092641108224659396765795452144064815761341321104087246151217134879547607066758663682702357666390897071886395518123041544718060193617760547848107588540156504758935787543246553706035451249171216368368607224982935938619089301863944851318
for i in range(211):
for w in range(211):
k = (hint1 + 0x114 * i) # 使用i作为偏移量
w_val = (hint2 + 0x514 * w) # 使用w作为偏移量,不应覆盖w
l = gcd(k - w_val, n)
if l != 1 and isPrime(l):
p = l
q = n // p
d = inverse(65537, (p-1)*(q-1))
m = pow(c, d, n)
print(long_to_bytes(m).decode())
利用枚举法来求出k,w,然后用**使用 gcd(k-w, n)
计算 k
和 w
之差的 n
的最大公约数。如果这个最大公约数 l
大于1,那么它可能是 n
的一个非平凡的因子,即 n
的一个素因子。**但是这个方法的缺点是有些时候运行不出来,因为枚举要消耗很多时间。
两个线性方程,我们一般会想到增广矩阵来求解
但没有找到适合的方法
第二部分:
实现的代码:
from Crypto.Util.number import inverse, long_to_bytes
from z3 import Solver, Int, sat
# 定义变量
n = 19270469180149377263192680520819033524539225081011510973771491132573055666673351141996751197354363664966014556774615485934908980461758850357009251309139628221564453417674382327302421186462670811373716926240975834774481469724971880623608600218091329795743254370563097739791612527201215958971410743353451459144002124470888119861714861743318989005059458006392282025661284787801335449493817479339656692022153914190452646349608988234249089757979295313780035505101668837926927936182966948338603241612244642741597658758777488950156533305392860253251286264242993704349899118371704510160880572747042643531951959235458650535201
c = 17922269792919020054615215743477596812624139562663477259751167464530271650542317088700713269485811397529339279516458231908605132062757375048865481634994627781161964719169079516071499023010331813470999183912373770424498490096501950489912324313610809253291227934210924561262506655227831816557706705271515382040988621473520356987673072721352470307538611049235274679949259625092930942227801261483611129856790200274136422472806003001953980483266780144214483509977936166152968674312795223620207142790023737286941892114758105363980960058996508562524135436561252121040044921523038783948500002980889791088501992627049267597054
hint = 19236929998880181808018278535269127648428289004903763893783918597013504816536557917002191668963373294797217170818500275763975145920076792641715979989044405220048466110491686882366659071341778289300858576719810757580192820193943223753948022827596919281442444812797548572128563878078576343846538381701558407690521650097077131251644013459573140665791665676168096886035764351033673931977943007462592700704650732813647930386012566936894017149333920203825492818595797754434803713959329583444899558835357904298974503703559166036391340551836013754747833877314394091456341194131951756729495427025667660222403204175396895401660
# 计算k
k = inverse(hint, n)
# 定义变量p和q
p = Int('p')
q = Int('q')
# 定义求解器和方程
s = Solver()
s.add(514 * p - 114 * q == k, p * q == n)
# 检查模型是否满足方程
if s.check() == sat:
model = s.model()
p1 = model[p].as_long()
q1 = model[q].as_long()
d = inverse(0x10001, (p1 - 1) * (q1 - 1))
m = pow(c, d, n)
print(long_to_bytes(int(m)).decode())
z3是一个定理证明器,定义一个求解器Slover,添加两个方程到里面然后验证我们的模型是否构造正确。Z3 作为一个定理证明器,可以验证给定的逻辑公式是否为真。在这种情况下,它被用来证明是否存在满足特定条件的整数 p
和 q。
在国赛的时候只得出了第二部分的思路但是没有找到正确的代码来实现,接下来的学习中还是侧重于优化算法,并且去回顾一下高等数学。