找到等于实数分数的自然数分数

Finding a natural number fraction equal to a real number fraction

本文关键字:自然数 实数 于实数      更新时间:2023-10-16

这更像是一个数学问题,但我认为这是正确的提问地点,它可能对某人有用。

以下是问题所要求的:

给定两个实数(rA 和 rB),找到最小的两个自然数(nA 和 nB),以便

rA/rB = nA/nB

对于那些没有正确理解的人来说,问题是要求一个等于某个给定实数的不可约分数。

当它变成一个精度问题并且需要花费大量时间才能找到这些数字(例如:rA = 665.32rB = 875.1)时,我的问题就出现了,我什至不知道是否有这样的自然数组合来匹配问题。

我还实现了一点KeyPress,以便您仍然可以检查nAnB获得了多少,而无需使控制台已满。

我想出的最有效的方法是:

#include <iostream>
#include <windows.h> // just for GetAsyncKeyState()
int main()
{
/** The two natural numbers*/
unsigned long long nA=1;
unsigned long long nB=1;
/** The two real numbers*/
double rA;
double rB;
std::cin >> rA >> rB;
bool bPairFound = false;
/** The maximum of which nA or nB could go. */
/** If the value is set to 0, the algorithm will stop when nA or nB overflows */
#define NUMBER_LIMIT 0x0
if ((double) nA / nB == rA / rB) bPairFound = true;
while(bPairFound == false)
{
if ((double) nA / nB > rA / rB) nB++;
if ((double) nA / nB < rA / rB) nA++;
if ((double) nA / nB == rA / rB) bPairFound = true;
/** A little keyPress that will show you how much nA and nB got. */
/** Press space while the program is running. */
if (GetAsyncKeyState(VK_SPACE) & 0x8000)
std::cout << "nA: "<<nA<<"   nB: " << nB << "  ---> "<< (double) nA / nB << "   " << rA / rB << std::endl;
if (nA <= NUMBER_LIMIT || nB <= NUMBER_LIMIT) break;
}
if (bPairFound == false) std::cout << "No pair could be found in the set limit." << std::endl;
if (bPairFound == true) std::cout << "nA: "<<nA<<"   nB: " << nB << std::endl;
return 0;
}

我的问题是:

  1. 我能让这个算法更有效吗?

  2. 如何将比较precision设置为 6 位数字?

  3. 有没有办法从一开始就确定unsigned long long范围内是否有这样的货币对?

编辑:这里有一些需要太多时间来解决或无法解决的例子。

rA = 1426.33rB = 12.7

rA = 764342.33rB = 98.02001

rA = 1.0001rB = 1.010001

除了精度问题(a)之外,你可以通过确保数字是整数,然后将它们除以最大公约数来最有效地做到这一点。

具体来说,伪代码,例如:

tA = rA
tB = rB
while tA != int(tA) or tB != int(tB):
tA = tA * 10
tB = tB * 10
gcd = calcGcd(tA, tB)
nA = tA / gcd
nB = tB / gcd

GCD实现应该很容易在Stack Overflow上找到。

事实上,这是我之前准备的:-)


(a)精度问题可以通过使用任意精度的算术库(如MPIR)来解决。

我相信这会更有效率。

while(bPairFound == false)
{
double left=(double)nA*rB;
double right=(double)nB*rA;
if(left>right){
double quotient=left/right;
unsigned long long prevB=nB;
nB*=quotient;
if(prevB==nB){
nB++;
}
}else if(right>left){
int quotient=right/left;
unsigned long long prevA=nA;
nA*=quotient;
if(prevA==nA){
nA++;
}
}else{
bPairFound = true;
}
/** A little keyPress that will show you how much nA and nB got. */
/** Press space while the program is running. */
if (GetAsyncKeyState(VK_SPACE) & 0x8000)
std::cout << "nA: "<<nA<<"   nB: " << nB << "  ---> "<< (double) nA / nB << "   " << rA / rB << std::endl;
if (nA <= NUMBER_LIMIT || nB <= NUMBER_LIMIT) break;
}

由于您正在按商增加数字,因此您将在早期过程中跳过相当多的步骤。 此外,此实现具有更多的乘法和更少的除法。由于乘法的收费较低,因此此方法将更有效。

我希望这有所帮助。

编辑 1: 我找到了一种提高系统精度的方法

double left=(double)nA*rB;
double right=(double)nB*rA;
double quotient=left/right;
unsigned long long test;
if(quotient>=1){
test=quotient*1000000;
}else{
test=100000/quotient;
}
if(test==1000000){
bPairFound = true;
}else if(left>right){
unsigned long long prevB=nB;
nB*=quotient;
if(prevB==nB){
nB++;
}
}else if(right>left){
unsigned long long prevA=nA;
nA*=1/quotient;
if(prevA==nA){
nA++;
}
}