咨询一个算法的问题,我是一点思路都没有了。

64 天前
 lostexile

想给女儿出一些计算题目,于是自己写了个简单的脚本。

现在遇到一个问题,我想要输出 5 个数字以内的四则运算算式,每个数字都有可能是 5 位数以内的自然数。

基于计算训练的需求,我想要保证结果必须为整数。 就是想出一些 131328÷27÷256=19 这种的算式,孩子自己就能快速的知道计算是不是有错误。

但是我发现在生成的算式的时候效率非常低,经常遇到卡死的情况。 于是我在生成前增加了一个校验的逻辑,就是输入一下配置,先看一下这个配置能生成多少个算式,如果符合条件的算式数量比较小,比如为 0 ,或者小于 10 ,就直接提示配置不合理。

现在我被这个检查配置的逻辑卡住了,完全没有思路如何高效的去计算符合配置的算式数量。 请问各位大佬有思路么。

1966 次点击
所在节点    问与答
24 条回复
corcre
64 天前
先生成乘法公式, 然后转换成除法, 例如生成 19*27*256=131328, 然后转换成除法 131328÷27÷256=19 🐶
Junzh
64 天前
我建议你反过来思考,先确定这个整体,再依次去乘。
coderluan
64 天前
反过来想啊,先随机一个整数,然后乘随机数,你只要确保结果不超出五位就行了吧。
weenhall5
64 天前
这个交给 ai 不是更好吗
Junzh
64 天前
先确定这个整数,如果全是除法的话,就反过来去乘。
lostexile
64 天前
AI 已经问过了,
目前 DeepSeek ,豆包,ChatGPT ,cursor ,火山已经全线阵亡,目前给我最靠谱的答案就是跑完结果之后缓存下来,下次再来请求直接用缓存的数据求和计算。


至于用乘法生成的方案,不是没考虑过,但是现在我有两个问题:
1. 先用乘法的话,我要如何控制被除数的位数,因为相当于乘法的两个因数都不知道位数的情况下去控制结果的位数。
2. 相同的逻辑,我想要生成小数乘以小数,但是结果必须为整数,也需要校验。


所以我想咨询一下有木有数学方面的大佬,有没有方面的我不了解的数学逻辑,可以直接计算的那种。
gg1025
64 天前
"每个数字都有可能是 5 位数以内的自然数" 建议直接打表
newaccount
64 天前
10^3 * 10^3 = 10^6
感觉上,反向计算乘法的时候,只要知道剩下可用的位数,就可以确定下一个数的取值范围
大概就是 pow(10, N),只要确定这个 N 的范围,然后 rand(N) 就可以?
另外为了保证结果算式能有不少于 M 个式子,每一步可以提前减去当前已生成的参数个数
suiterchik
64 天前
如果不需要支持括号优先级的话应该还好?初始化一个随机的数值 x1 ,并创建一个 answer 变量 answer = 1

然后从加减乘除中随机选一个操作

* 如果是加法,则下个数字 x2 的取值范围是 [0, 99999 - answer] 中随机取一个整数,确保 answer + x2 < 99999 ,如果找不到则这一步步采用加法;

* 如果是减法,则下个数字 x2 的取值范围是 [0, answer] 中随机取一个数,确保 answer - x2 > 0 ,如果找不到则这一步不采用减法;

* 如果是乘法,则下个数字 x2 的取值范围是 [0, 99999 / answer], 随机取一个整数,确保 answer * x2 < 99999 ,如果找不到则这一步不采用乘法;

* 如果是除法,则计算 answer 的公约数,然后从公约数列表 [y1, y2, y3...] 中任取 k 个乘起来作为 x2 ,确保 answer / x2 为整数,如果 answer 为素数则这一步不采用除法

然后根据你这步采用哪个 op ,以及对应的数值,更新 answer 变量,进入下一个数字 x3 的操作,x3 的选取与 x2 的逻辑一样,重复直到 x5 选取完毕
suiterchik
64 天前
@suiterchik answer 变量初始化应该是 answer = x1 ,敲少了个字母
play78
64 天前
你有没有想过现在的计算机已经很强大了。随便暴力都可以生成出来。如果只是为了给小孩出题,那怎么简单怎么来。如果要研究纯数学算法,那就比较难了。
```js
for(var i=0; i<10000; i++){
var a = Math.floor(Math.random()*99999);
var b = Math.floor(Math.random()*99999);
var c = Math.floor(Math.random()*99999);
var f = "+-*/";
var d1 = f[Math.floor(Math.random()*f.length)];
// var d2 = f[Math.floor(Math.random()*f.length)];
var d2;
do {
d2 = f[Math.floor(Math.random()*f.length)];
} while (d1 !== '/' && d2 !== '/'); //保证一定包含除法
var exp = a+d1+b+d2+c;
var v = eval(exp);
if(v % 1 !== 0){
continue;
}
if(v > 99999 || v < 0){
continue;
}
console.log(exp + "=" + v);
}
```
39151+97515/55=40924
77223-34790/71=76733
72451-64843/61=71388
43332/471+74553=74645
34143*66638/68286=33319
LandCruiser
64 天前
先生成乘法就蛮好的,5 位数最大是 99999 ,max = 99999 所以 a,b = random ( 0 ,max ) if a*b <max; print [a]*[b] /a = a*b
然后还可以优化,比如 if a*b>max; a = a - 1 b= b -1
suiterchik
64 天前
@suiterchik 噢这里还有个问题,乘除的优先级比加法高,所以你还需要把 x1, x2, .., x5 构建出 ast ,然后再解析成正确的表达式,否则会出现 1 + 2 x 3 = 9 的错误
piku
64 天前
我想要输出 5 个数字以内的四则运算算式,每个数字都有可能是 5 位数以内的自然数。
基于计算训练的需求,我想要保证结果必须为整数。
这块没看懂,是说有 a b c d e 这样 5 个随机整数或小数且每个数字都<=99999 ,配合+-×÷4 个随机运算符号,运算结果也<=99999 ?
假设完全随机穷举出了符合要求的算式且结果不为整数,那么将结果取整后反推一下,改变 e 的值就好了啊?比如(3+4)/6=1.1666 ,取整为 1 ,反推纠正为( 3+4 )/7=1
GeruzoniAnsasu
64 天前
? 这还能卡死

随机一个数作为答案,再随机几个步骤得到初始数字,再根据优先级规则打乱顺序并补上括号

如果发现数字过大,那就每个中间数字比较一下离数字域上限有多近,乘一个权重来随机是往上涨还是往下缩

如果发现经常摇不出可以整除的数字就动态规划一下,把当前数字乘上来的路径记住,从可行路径里选一个

如果可选路径数量太少就先因式分解打一个表( 2*2,2*3,2*5,...2*2*2,2*2*3,...)
Gavin999
64 天前
搞这么麻烦 还写脚本 一个计算器不就解决了吗
tool2dx
64 天前
小学计算题,代码怎么可能卡死,我是非常不理解.

用乘法去倒推,暴力循环都能搞定的事情.
geelaw
64 天前
很神秘,因为看完主贴之后我的问题是:什么是“配置”?不交代什么是“配置”则连要解决的问题是什么都不知道。

很多没有思路是从没有清楚表述问题开始的。
kamilic
64 天前
我觉得你可以通过遍历预先生成一大堆随机相乘的式子和结果( axb, axbxc, axbxcxd, .....)。
乘法问题直接在集合里面随机取,除法问题可以通过乘法问题进行转换得到。
加减法本身就能自由组合,你甚至可以把乘法中的数字拆成加减法问题做一点变种,只要等号两侧能配平随便你玩。
JoeJoeJoe
64 天前
直接用 ai 生成了一个 python 的:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Author: Joe
# Description: 生成 10000 以内的小学计算题

import random

def generate_operation():
"""生成一个四则运算表达式,保证结果为整数"""
operators = ['+', '-', '*', '÷'] # 将 '/' 替换为 '÷'
# 计算长度 1 到 5 次
count = random.randint(1, 5)
# 减少小数出现的次数,约 30% 的概率为小数
numbers = []
for _ in range(count + 1):
if random.random() < 0.3:
num = round(random.uniform(1, 10000), 2) # 最多保留两位小数
else:
num = random.randint(1, 10000)
numbers.append(num)

ops = [random.choice(operators) for _ in range(count)]

expression = str(numbers[0])
for i in range(count):
while ops[i] == '÷' and numbers[i + 1] == 0: # 修改为 '÷'
if random.random() < 0.3:
numbers[i + 1] = round(random.uniform(1, 10000), 2)
else:
numbers[i + 1] = random.randint(1, 10000)
# 确保除法运算结果为整数
if ops[i] == '÷': # 修改为 '÷'
# 计算商并确保至少为 1
quotient = max(1, int(numbers[i] // numbers[i + 1]))
# 调整被除数使其为除数的整数倍
numbers[i] = numbers[i + 1] * random.randint(1, quotient)
expression += f" {ops[i]} {numbers[i + 1]}"

result = eval(expression.replace('÷', '/')) # 计算时将 '÷' 替换回 '/'
# 确保结果为整数
while isinstance(result, float) and not result.is_integer():
numbers = []
for _ in range(count + 1):
if random.random() < 0.3:
num = round(random.uniform(1, 10000), 2)
else:
num = random.randint(1, 10000)
numbers.append(num)
ops = [random.choice(operators) for _ in range(count)]
expression = str(numbers[0])
for i in range(count):
while ops[i] == '÷' and numbers[i + 1] == 0: # 修改为 '÷'
if random.random() < 0.3:
numbers[i + 1] = round(random.uniform(1, 10000), 2)
else:
numbers[i + 1] = random.randint(1, 10000)
if ops[i] == '÷': # 修改为 '÷'
quotient = max(1, int(numbers[i] // numbers[i + 1]))
numbers[i] = numbers[i + 1] * random.randint(1, quotient)
expression += f" {ops[i]} {numbers[i + 1]}"
result = eval(expression.replace('÷', '/')) # 计算时将 '÷' 替换回 '/'

return expression, int(result)

# 生成题目和结果
expression, result = generate_operation()
# 打印题目
print(f"题目: {expression}")
# 打印结果
print(f"结果: {result}")

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1144608

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX