我知道2的幂可以使用<<运营商。 10的力量怎么样?喜欢10 ^ 5?在C ++中有没有比pow(10,5)更快的方法?这是一个非常直接的计算手工。但由于数字的二进制表示,计算机似乎并不容易......让我们假设我只对整数幂10 ^ n感兴趣,其中n是整数。
答案 0 :(得分:27)
这样的事情:
int quick_pow10(int n)
{
static int pow10[10] = {
1, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000
};
return pow10[n];
}
显然,long long
可以做同样的事情。
这应该比任何竞争方法快几倍。但是,如果你有很多基础,它是非常有限的(虽然数值的数量在较大的基数下显着下降),所以如果没有大量的组合,它仍然是可行的。
作为比较:
#include <iostream>
#include <cstdlib>
#include <cmath>
static int quick_pow10(int n)
{
static int pow10[10] = {
1, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000
};
return pow10[n];
}
static int integer_pow(int x, int n)
{
int r = 1;
while (n--)
r *= x;
return r;
}
static int opt_int_pow(int n)
{
int r = 1;
const int x = 10;
while (n)
{
if (n & 1)
{
r *= x;
n--;
}
else
{
r *= x * x;
n -= 2;
}
}
return r;
}
int main(int argc, char **argv)
{
long long sum = 0;
int n = strtol(argv[1], 0, 0);
const long outer_loops = 1000000000;
if (argv[2][0] == 'a')
{
for(long i = 0; i < outer_loops / n; i++)
{
for(int j = 1; j < n+1; j++)
{
sum += quick_pow10(n);
}
}
}
if (argv[2][0] == 'b')
{
for(long i = 0; i < outer_loops / n; i++)
{
for(int j = 1; j < n+1; j++)
{
sum += integer_pow(10,n);
}
}
}
if (argv[2][0] == 'c')
{
for(long i = 0; i < outer_loops / n; i++)
{
for(int j = 1; j < n+1; j++)
{
sum += opt_int_pow(n);
}
}
}
std::cout << "sum=" << sum << std::endl;
return 0;
}
使用-Wall -O2 -std=c++0x
编译g ++ 4.6.3,得到以下结果:
$ g++ -Wall -O2 -std=c++0x pow.cpp
$ time ./a.out 8 a
sum=100000000000000000
real 0m0.124s
user 0m0.119s
sys 0m0.004s
$ time ./a.out 8 b
sum=100000000000000000
real 0m7.502s
user 0m7.482s
sys 0m0.003s
$ time ./a.out 8 c
sum=100000000000000000
real 0m6.098s
user 0m6.077s
sys 0m0.002s
(我确实可以选择使用pow
,但是当我第一次尝试使用它时需要1m22.56,所以当我决定优化循环变体时我将其删除了)
答案 1 :(得分:12)
有一些方法可以比使用std::pow()
更快地计算10的整数幂!第一个实现是pow(x, n)
可以在O(log n)时间内实现。下一个实现是pow(x, 10)
与(x << 3) * (x << 1)
相同。当然,编译器知道后者,即,当您将整数乘以整数常量10时,编译器将执行最快的任何乘以10.根据这两个规则,很容易创建快速计算,即使x
是一个大整数类型。
如果您对此类游戏感兴趣:
答案 2 :(得分:10)
使用模板元编程的任何基础的解决方案:
template<int E, int N>
struct pow {
enum { value = E * pow<E, N - 1>::value };
};
template <int E>
struct pow<E, 0> {
enum { value = 1 };
};
然后它可用于生成可在运行时使用的查找表:
template<int E>
long long quick_pow(unsigned int n) {
static long long lookupTable[] = {
pow<E, 0>::value, pow<E, 1>::value, pow<E, 2>::value,
pow<E, 3>::value, pow<E, 4>::value, pow<E, 5>::value,
pow<E, 6>::value, pow<E, 7>::value, pow<E, 8>::value,
pow<E, 9>::value
};
return lookupTable[n];
}
这必须与正确的编译器标志一起使用,以便检测可能的溢出。
用法示例:
for(unsigned int n = 0; n < 10; ++n) {
std::cout << quick_pow<10>(n) << std::endl;
}
答案 3 :(得分:4)
整数幂函数(不涉及浮点转换和计算)可能比pow()
更快:
int integer_pow(int x, int n)
{
int r = 1;
while (n--)
r *= x;
return r;
}
编辑:基准测试 - 天真整数取幂方法似乎优于浮点数大约两倍:
h2co3-macbook:~ h2co3$ cat quirk.c
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <math.h>
int integer_pow(int x, int n)
{
int r = 1;
while (n--)
r *= x;
return r;
}
int main(int argc, char *argv[])
{
int x = 0;
for (int i = 0; i < 100000000; i++) {
x += powerfunc(i, 5);
}
printf("x = %d\n", x);
return 0;
}
h2co3-macbook:~ h2co3$ clang -Wall -o quirk quirk.c -Dpowerfunc=integer_pow
h2co3-macbook:~ h2co3$ time ./quirk
x = -1945812992
real 0m1.169s
user 0m1.164s
sys 0m0.003s
h2co3-macbook:~ h2co3$ clang -Wall -o quirk quirk.c -Dpowerfunc=pow
h2co3-macbook:~ h2co3$ time ./quirk
x = -2147483648
real 0m2.898s
user 0m2.891s
sys 0m0.004s
h2co3-macbook:~ h2co3$
答案 4 :(得分:2)
这是一个刺痛:
// specialize if you have a bignum integer like type you want to work with:
template<typename T> struct is_integer_like:std::is_integral<T> {};
template<typename T> struct make_unsigned_like:std::make_unsigned<T> {};
template<typename T, typename U>
T powT( T base, U exponent ) {
static_assert( is_integer_like<U>::value, "exponent must be integer-like" );
static_assert( std::is_same< U, typename make_unsigned_like<U>::type >::value, "exponent must be unsigned" );
T retval = 1;
T& multiplicand = base;
if (exponent) {
while (true) {
// branch prediction will be awful here, you may have to micro-optimize:
retval *= (exponent&1)?multiplicand:1;
// or /2, whatever -- `>>1` is probably faster, esp for bignums:
exponent = exponent>>1;
if (!exponent)
break;
multiplicand *= multiplicand;
}
}
return retval;
}
以上是一些事情。
首先,所以BigNum的支持很便宜,它是template
。开箱即用,它支持任何支持*= own_type
的基类型,并且可以隐式转换为int
,或者int
可以隐式转换为它(如果两者都是真的,问题将会出现发生),你需要专门化一些template
来表明所涉及的指数类型是无符号和类似整数。
在这种情况下,类似整数和无符号意味着它支持&1
返回bool
和>>1
返回可以构造的内容并最终(在重复>>1
之后) s)达到在bool
上下文中评估它返回false
的点。我使用traits类来表达限制,因为像-1
这样的值的天真使用会编译并且(在某些平台上)永远循环,而(在其他平台上)则不会。
此算法的执行时间(假设乘法为O(1))为O(lg(指数)),其中lg(指数)是<<1
exponent
所需的次数在false
ean上下文中评估为bool
之前。对于传统的整数类型,这将是exponent
s值的二进制日志:所以不超过32。
我还消除了循环中的所有分支(或者,使现有的编译器显然不需要分支,更准确地说),只有控制分支(统一为真,直到它为假一次)。对于高基数和低指数而言,甚至可能消除该分支可能是值得的......
答案 5 :(得分:1)
您可以使用最快的查找表
您还可以考虑使用this: -
template <typename T>
T expt(T p, unsigned q)
{
T r(1);
while (q != 0) {
if (q % 2 == 1) { // q is odd
r *= p;
q--;
}
p *= p;
q /= 2;
}
return r;
}
答案 6 :(得分:1)
没有乘法而且没有表格版本:
//Nx10^n
int Npow10(int N, int n){
N <<= n;
while(n--) N += N << 2;
return N;
}
答案 7 :(得分:1)
此函数将比pow更快地计算x ^ y。如果是整数值。
int pot(int x, int y){
int solution = 1;
while(y){
if(y&1)
solution*= x;
x *= x;
y >>= 1;
}
return solution;
}
答案 8 :(得分:0)
基于Mats Petersson方法,但编译时生成缓存。
#include <iostream>
#include <limits>
#include <array>
// digits
template <typename T>
constexpr T digits(T number) {
return number == 0 ? 0
: 1 + digits<T>(number / 10);
}
// pow
// https://stackoverflow.com/questions/24656212/why-does-gcc-complain-error-type-intt-of-template-argument-0-depends-on-a
// unfortunatly we can't write `template <typename T, T N>` because of partial specialization `PowerOfTen<T, 1>`
template <typename T, uintmax_t N>
struct PowerOfTen {
enum { value = 10 * PowerOfTen<T, N - 1>::value };
};
template <typename T>
struct PowerOfTen<T, 1> {
enum { value = 1 };
};
// sequence
template<typename T, T...>
struct pow10_sequence { };
template<typename T, T From, T N, T... Is>
struct make_pow10_sequence_from
: make_pow10_sequence_from<T, From, N - 1, N - 1, Is...> {
//
};
template<typename T, T From, T... Is>
struct make_pow10_sequence_from<T, From, From, Is...>
: pow10_sequence<T, Is...> {
//
};
// base10list
template <typename T, T N, T... Is>
constexpr std::array<T, N> base10list(pow10_sequence<T, Is...>) {
return {{ PowerOfTen<T, Is>::value... }};
}
template <typename T, T N>
constexpr std::array<T, N> base10list() {
return base10list<T, N>(make_pow10_sequence_from<T, 1, N+1>());
}
template <typename T>
constexpr std::array<T, digits(std::numeric_limits<T>::max())> base10list() {
return base10list<T, digits(std::numeric_limits<T>::max())>();
};
// main pow function
template <typename T>
static T template_quick_pow10(T n) {
static auto values = base10list<T>();
return values[n];
}
// client code
int main(int argc, char **argv) {
long long sum = 0;
int n = strtol(argv[1], 0, 0);
const long outer_loops = 1000000000;
if (argv[2][0] == 't') {
for(long i = 0; i < outer_loops / n; i++) {
for(int j = 1; j < n+1; j++) {
sum += template_quick_pow10(n);
}
}
}
std::cout << "sum=" << sum << std::endl;
return 0;
}
代码不包含quick_pow10,integer_pow,opt_int_pow以提高可读性,但在代码中使用它们进行了测试。
使用gcc版本4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5)编译,使用-Wall -O2 -std = c ++ 0x,得到以下结果:
$ g++ -Wall -O2 -std=c++0x main.cpp
$ time ./a.out 8 a
sum=100000000000000000
real 0m0.438s
user 0m0.432s
sys 0m0.008s
$ time ./a.out 8 b
sum=100000000000000000
real 0m8.783s
user 0m8.777s
sys 0m0.004s
$ time ./a.out 8 c
sum=100000000000000000
real 0m6.708s
user 0m6.700s
sys 0m0.004s
$ time ./a.out 8 t
sum=100000000000000000
real 0m0.439s
user 0m0.436s
sys 0m0.000s
答案 9 :(得分:0)
现在,使用constexpr
,您可以这样做:
constexpr int pow10(int n) {
int result = 1;
for (int i = 1; i<=n; ++i)
result *= 10;
return result;
}
int main () {
int i = pow10(5);
}
i
将在编译时计算。为x86-64 gcc 9.2生成的ASM:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 100000
mov eax, 0
pop rbp
ret
答案 10 :(得分:0)
如果要计算,例如10 ^ 5,则可以:
int main() {
cout << (int)1e5 << endl; // will print 100000
cout << (int)1e3 << endl; // will print 1000
return 0;
}
答案 11 :(得分:0)
result *= 10
也可以写成result = (result << 3) + (result << 1)
constexpr int pow10(int n) {
int result = 1;
for (int i = 0; i < n; i++) {
result = (result << 3) + (result << 1);
}
return result;
}