Given an array of n
non-negative elements, is there a function in any library for C/C++ that returns the smallest positive multiplier that when applied to each element of the array returns an integer number?
For example, if the array with n=2
is 1.66667, 2.33333
, the multiplier would be 3. For when we multiply each element of the array with 3, we get 5, 7
, both integer.
If the array is 8,10
, the multiplier would be 0.5. That would give us 4,5
.
(1) Is there an efficient function for this in any of the well-known libraries such as boost
, eigen
, etc.?
(2) In case there is nothing available in libraries, what is an efficient algorithm for figuring out the multiple?
答案 0 :(得分:6)
There is no good solution to your problem in the general case because the values are stored in a floating point format that has finite precision and can only store fractions with denominators powers 2 exactly. For example 0.1 * 10
may very well not be an integral value on your platform.
If you compute the values in the array from integral quantities, you should represent them as normalized fractions with pairs of adequately sized integers and compute the least common multiple of their denominators.
If you want an approximate solution to the problem, you can specify the value of epsilon, and design a solution by hand. I can think of no library function to fit this need, but a brute force solution is easy to write:
unsigned long long ullgcd(unsigned long long a, unsigned long long b) {
/* compute the greatest common divisor using Euclid's elegant method */
if (a < b)
return ullgcd(b, a);
else
if (b == 0)
return a;
else
return ullgcd(b, a % b);
}
double least_multiple(double *a, size_t n, double epsilon) {
for (double mult = 1;; mult += 1) {
size_t i;
unsigned long long div = 0;
for (i = 0; i < n; i++) {
double d = fabs(a[i] * mult);
unsigned long long v = round(d);
if (fabs(v - d) > epsilon)
break;
div = ullgcd(v, div);
}
if (i == n)
break;
}
/* mult is the smallest non zero integer that fits your goal.
the smallest multiplier is obtained by dividing it
by the greatest common divisor of the resulting integer array.
*/
return mult / div;
}
答案 1 :(得分:4)
Not in the way you've specified no. The problem is the decimal values 1.66667
and 2.33333
don't have such a multiplier: you are assuming an approximation which stems from, what is from a mathematical perspective, an arbitrary rounding policy.
And then there are the floating point properties to worry about, so you can rule out anything using a double
or a float
.
Your best bet here is to use a fraction class to represent the numbers. Then any common multiplier will drop out with some simple mathematics.
See http://www.boost.org/doc/libs/1_64_0/libs/rational/index.html
答案 2 :(得分:1)
看看Rosetta Code的"Convert decimal number to rational."因为我不熟悉C,我将C代码转换为Python(即使我认为Python可能有一些相关的库)并添加了几个函数很容易适应C.如果分数转换中的分母都是1.0
,我们除以数字列表的最大公约数。否则,我们会返回独特分母的产品。
import math
import operator
def f(arr):
epsilon = 4
fractions = [rat_approx(i, 16**epsilon) for i in arr]
# The denominators in the fraction conversion are all 1.0
if sum([denom for (num, denom) in fractions]) == len(fractions):
return 1.0 / gcd_of_many([num for (num, denom) in fractions])
else:
# Otherwise, return the product of unique denominators
return reduce(operator.mul, set([denom for (num, denom) in fractions]), 1)
def gcd(a, b):
if a < b:
return gcd(b, a)
elif b == 0:
return a;
else:
return gcd(b, a % b)
def gcd_of_many(arr):
result = arr[0]
for i in xrange(1, len(arr)):
result = gcd(result, arr[i])
return result
# Converted from
# https://rosettacode.org/wiki/Convert_decimal_number_to_rational#C
def rat_approx(f, md):
# a: continued fraction coefficients.
h = [0, 1, 0]
k = [1, 0, 0]
n = 1
neg = 0
num = 0
denom = 0
if md <= 1:
denom = 1
num = f
return num, denom
if f < 0:
neg = 1
f = -f
while f != math.floor(f):
n <<= 1
f *= 2
d = f
# continued fraction and check denominator each step
for i in xrange(65):
a = d // n if n else 0
if i and not a:
break
x = d
d = n
n = x % n
x = a;
if k[1] * a + k[0] >= md:
x = (md - k[0]) // k[1]
if x * 2 >= a or k[1] >= md:
i = 65
else:
break
h[2] = x * h[1] + h[0]
h[0] = h[1]
h[1] = h[2]
k[2] = x * k[1] + k[0]
k[0] = k[1]
k[1] = k[2]
denom = k[1]
num = -h[1] if neg else h[1]
return (num, denom)
输出:
f([8, 10])
=> 0.5
f([1.66667, 2.33333])
=> 3.0