我有等式:
2^y = 2^q0 + ... 2^qn
n是任意整数(有任意数量的'q')。 'q'的值可以大到500,其中2 ^ q无法存储在整数或长变量类型中。我想计算“y”而不是下面的方法,因为存储容量问题:
log2(2^q0 + ... + 2^qn)
如何在C ++中以有效的方式计算'y'。无论如何,在'q'上用简单的算术运算来计算'y'?
编辑:'q是非负的,我找这个问题的2个版本:'q是整数,'q是双答案 0 :(得分:13)
首先排序qi
。假设最小为p
,从所有p
中减去qi
。你可以检查qi
是否形成算术系列,如果你很幸运并且他们形成了这样的系列,你有一个数学捷径,但是否则因为AFAIK没有数学规则来简化log(a1 + a2 + ... + ak)
计算y
的最佳方法是:
由于您已对qi
进行了排序,因此您可以以类似动态算法的方式计算sum = 1 + 2 ^ (q1-p) + 2 ^ (q2-p) + ...
(即使用以前的结果来计算下一个术语)。
prev_exp = 0;
prev_term = 1;
this_term = 0;
sum = 1;
// p is previously subtracted from q[i]s
for (int i = 1; i < n; ++i) {
this_term = prev_term * (1 << (q[i] - prev_exp)); // 2 ^ m = (1 << m)
prev_term = this_term;
prev_exp = q[i] - prev_exp;
sum += this_term;
}
y
可以计算为y = p + log2(sum)
请注意,您还要先对小数字求和。这将有助于浮点精度。
我正在编辑这个答案,根据分而治之的算法类型添加另一个解决方案,但我无法完成它,但我猜我是否将它留在隐藏的块中(此站点的编辑器命名中的扰流块)有人可以完成或改进这部分答案。随时编辑。
如果
q[i]
s的最大值大于它们的最小值(即p
),你可以使用分而治之算法,递归计算sum1 = 1 + 2^(q[1]-p) + .... + 2^(q[n/2]-p)
和sum2 = 2^(q[n/2 + 1]-p) + ... + 2 ^ (q[n-1] - p)
您可以分解2^(q[n/2 + 1]-p)
这里也。然后你会有:y = p + log2(sum1 + sum2) = p + log2(sum1 + 2^p' sum2')
其中p'
是q[n/2 + 1]-p
。它可以帮助您减少数字。
答案 1 :(得分:1)
这显然是标准或内置类型无法提供的问题。
您可以观察到,2 ^ qx是左侧1个移位的qx位,log2(y)是在数字变为1之前必须采取的右移位数。 然后你可以观察到在溢出中添加两个数字会使结果小于addns,并且在左边传播一个数字。
此时你可以:
unsigned x[1+500/sizef(unsigned)])
)unsigned q
设置适当的位(将q除以sizeof(无符号)以确定索引,并使用modulous来确定剩余的移位)如果你能确保各种qi不重复,就不需要这样的算术:你要做的只是记住最高的那个。
答案 2 :(得分:0)
你不需要取幂或乘以2的整数幂:只需将1移动适当的量。我认为以下代码效率最高:
double ComputeY( vector<unsigned> Qs )
{
unsigned sum = 0;
for( vector<unsigned>::iterator it = Qs.begin(); it != Qs.end(); ++it )
{
int q = *it; // Get the next q
int two_power_q = 1 << q; // This is the key: 2^q == 1 << q
sum += two_power_q;
}
double y = log2( sum ); // need double because the result may not be integer
return y;
}
答案 3 :(得分:0)
此解决方案类似于MJafar Mash的解决方案,但避免无穷大(例如:如果相邻的指数有巨大差异{0,1010})。
#include <algorithm>
#include <cmath>
#include <iostream>
#include <limits>
#include <vector>
#include <iostream>
#include <type_traits>
template <typename T>
double logarithm(const std::vector<T>& sorted_exponents) {
typedef typename std::make_unsigned<T>::type Unsigned;
const unsigned digits = 32;
// Evaluate sum(2^sorted_exponents[i]) = sum * 2^scale*digits
double sum = 0;
Unsigned scale = 0;
for(Unsigned e : sorted_exponents) {
// Evaluate e = 2^e * 2^(s*digits)
Unsigned s = e / digits;
e %= digits;
sum = double(uint32_t(1) << e) + sum / pow(2.0, double(s - scale)*digits);
scale = s;
}
return std::log2(sum) + double(scale)*digits;
}
int main()
{
std::cout.precision(std::numeric_limits<double>::digits10 + 1);
std::vector<int> v;
unsigned size = 500;
for(unsigned max_exponent = 1; max_exponent < 10000000; max_exponent *= 10) {
v.clear();
v.reserve(size);
for(unsigned i = 0; i < size; ++i)
v.push_back(std::rand() % max_exponent);
std::sort(v.begin(), v.end());
double log = logarithm(v);
std::cout << "Maximal Exponent: " << v.back() << std::endl;
std::cout << " Logarithm: " << log;
std::cout << std::endl;
}
}
结果:
Maximal Exponent: 0
Logarithm: 8.965784284662087
Maximal Exponent: 9
Logarithm: 15.58590144969077
Maximal Exponent: 99
Logarithm: 102.4678312951325
Maximal Exponent: 997
Logarithm: 997.5899323737988
Maximal Exponent: 9999
Logarithm: 9999.000000002708
Maximal Exponent: 99960
Logarithm: 99960.32192809488
Maximal Exponent: 998055
Logarithm: 998055
答案 4 :(得分:0)
让我们消化这里的问题。
qn binary
2^0 => 00000001
2^1 => 00000010
2^2 => 00000100
---------------
sum => 00000111
y => log2(sum) => 2
这意味着在总结所有2 ^ q0 + ... 2 ^ qn后,它询问最左边的二进制1的位置在哪里 和qn是每个2 ^ qn中二进制数字1的位置。
其他人已经指出,如果q中的所有都是唯一的,则y = max_element(q)。
如果不是这种情况,我们必须计算总和并找到最左边的二进制数字1。 要计算总和,我们不需要保留所有数字,只保留最左边的数字。
让我们将每个2 ^ qn转换为二进制,并将q中的每个元素按数字1所在的位置分组。 如果数字1落入前48位,则将其置于组0中,如果它落入第二位48位,则将其置于组1中,依此类推。 然后我们计算组0的总和并将所有溢出数字转发到下一组,直到我们计算最后一组。
选择48位数组,以便可以使用int64计算,8位MSB位为carrie forward。
在最后一组中,找到最左边数字1的位置: left_most_bit
然后 y = left_most_bit + group_number * 48
int function(vector<int> const& q)
{
vector<int> group;
// group each element in q
for(int i=0; i<q.size(); ++i)
{
group[i] = q[i] / 48;
}
// find the maximum group
int group_max = std::max_element(group.begin(), group.end());
int64 sum = 0;
for(int g=0; g<=group_max; ++g)
{
// calculate group of 48 bits
for(int i=0; i<q.size(); ++i)
{
if(group[i] == g) // is this elementin the group that we are calculating?
{
int pos_of_digit_1 = q[i] - g * 48;
sum += (1 << pos_of_digit_1); // convert to value and sum it.
}
}
sum = sum >> 48; // carrie forward the 8 MSB bits
}
int pos_of_left_most_1 = 0;
for(int i=0; i<64; ++i)
{
if((sum << i) & 0x8000000000000000);
{
pos_of_left_most_1 = 64 - i;
break;
}
}
return group_max * 48 + pos_of_left_most_1;
}
这个答案是针对整数值y和q的。对于浮点值,我们不能这样做。