用python将数字表示为2的幂的和的最快方法是什么

时间:2018-08-10 12:15:38

标签: python binary numbers

例如

>>> two_powers(42)
>>> (2, 8, 32)

我当前的幼稚实现(取自here)看起来像这样

def two_powers(num):
    return tuple(2 ** i for i, j in enumerate(bin(num)[-1: 1: -1]) if j == '1')

但是我希望有更快的方法。

8 个答案:

答案 0 :(得分:5)

尝试一下:

def two_powers(num):
    powers = []
    while num != 0:
        powers.append(num & -num)
        num = num & (num - 1)
    return powers

答案 1 :(得分:1)

您可以这样做:

import math

def two_powers(num):
    # Compute number of bits for big numbers
    num_bits = math.floor(math.log2(num)) + 1 if num >= (1 << 32) else 32
    # Take those bits where there is a "one" in the number
    return [1 << p for p in range(num_bits) if num & (1 << p)]

print(two_powers(42))
# [2, 8, 32]

编辑:请输入位数,如果您真正关心性能,可以进行更多拆分,或者降低以节省迭代次数,或者提高以避免计算对数(或者如果您知道输入数字将在某些位置特定范围):

import math

def two_powers(num):
    # Compute number of bits for big numbers
    if num < (1 << 8):
        num_bits = 8
    elif num < (1 << 16):
        num_bits = 16
    elif num < (1 << 24):
        num_bits = 24
    elif num < (1 << 32):
        num_bits = 32
    else:
        num_bits = math.floor(math.log2(num)) + 1
    # Take those bits where there is a "one" in the number
    return [1 << p for p in range(num_bits) if num & (1 << p)]

print(two_powers(42))
# [2, 8, 32]

答案 2 :(得分:1)

您的解决方案实际上是好的,但有一点点(很大!)效率方面的细节:

使用

1<<i 

(按位移位)而不是

2**i

因此,要复制您的内容,请考虑以下事项:

def two_powers(num):
    return set(1 << i for i, j in enumerate(bin(num)[-1: 1: -1]) if j == '1')
print two_powers(42)

答案 3 :(得分:1)

您可以使用log2和生成器表达式 直到用完两次的幂。

import math

def two_powers(num):
    while num > 0:
        power = int(math.log(num, 2))
        yield 2**power
        num = num - 2**power

样品运行:

>>> tuple(two_powers(42)))
(32, 8, 2)
>>> tuple(two_powers(43)))
(32, 8, 2, 1)

答案 4 :(得分:1)

  

收件人:后期发帖人:请随时运行此小型基准测试   包括您的代码,并相应地修改结果。你会需要   重新进行测试,以适应所有人的硬件差异。

先生们,这是你的成绩:

获胜者是@tsionyx

timeit:

Benchmark <=10^2
[97, 48, 31, 39, 33, 69, 71, 21, 50, 17]
two_powers_op_____ 17.8 µs ± 1.2 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_jdehesa 15.1 µs ± 888 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_matina_ 14.6 µs ± 755 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_agile_e 7.87 µs ± 524 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_taras__ 25.8 µs ± 1.29 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_sunitha 12 µs ± 1.19 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_blhsing 11.5 µs ± 566 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_tsionyx 5.77 µs ± 57.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Benchmark <=10^3
[682, 124, 42, 275, 743, 837, 474, 186, 739, 290]
two_powers_op_____ 22.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_jdehesa 17.9 µs ± 829 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_matina_ 17.6 µs ± 881 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_agile_e 12.7 µs ± 763 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_taras__ 49.2 µs ± 3.85 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_sunitha 18.1 µs ± 2.56 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_blhsing 19.2 µs ± 2.79 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_tsionyx 10.4 µs ± 1.14 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Benchmark <=10^4
[4641, 5675, 3355, 4746, 9948, 5192, 3446, 7174, 1683, 7611]
two_powers_op_____ 30.8 µs ± 3.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_jdehesa 22.2 µs ± 2.37 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_matina_ 21.7 µs ± 1.13 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_agile_e 17.5 µs ± 2.46 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_taras__ 64.3 µs ± 12.1 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_sunitha 18.5 µs ± 1.24 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_blhsing 19.2 µs ± 193 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_tsionyx 11.6 µs ± 43.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Benchmark <=10^5
[20885, 23810, 25330, 32967, 34183, 16847, 54905, 85767, 37069, 32379]
two_powers_op_____ 32.8 µs ± 1.76 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_jdehesa 24.2 µs ± 534 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_matina_ 27.1 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_agile_e 18.7 µs ± 246 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
two_powers_taras__ 68.9 µs ± 3.16 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_sunitha 20.6 µs ± 486 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_blhsing 22.7 µs ± 883 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_tsionyx 14.2 µs ± 244 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Benchmark <=10^6
[182928, 93105, 710309, 926572, 859733, 818327, 654197, 829750, 358363, 946684]
two_powers_op_____ 40.6 µs ± 236 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_jdehesa 28.2 µs ± 310 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_matina_ 27.9 µs ± 936 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_agile_e 23.8 µs ± 364 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_taras__ 89.9 µs ± 406 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_sunitha 24.4 µs ± 493 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_blhsing 26.6 µs ± 366 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
two_powers_tsionyx 19.3 µs ± 95.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

使用的代码:

import functools
import math
import random
from collections import defaultdict


def two_powers_op_____(num):
    return tuple(2 ** i for i, j in enumerate(bin(num)[-1: 1: -1]) if j == '1')


def two_powers_jdehesa(num):
    num_bits = math.floor(math.log2(num)) + 1
    return [1 << p for p in range(num_bits) if num & (1 << p)]


def two_powers_matina_(num):
    return set(1 << i for i, j in enumerate(bin(num)[-1: 1: -1]) if j == '1')


def two_powers_agile_e(num):
    powers = []
    while num != 0:
        powers.append(num & -num)
        num = num & (num - 1)
    return powers


def _two_powers_taras(num):
    while num > 0:
        power = int(math.log(num, 2))
        yield 2 ** power
        num = num - 2 ** power


def two_powers_taras__(num):
    return tuple(_two_powers_taras(num))


def two_powers_sunitha(num):
    return {1 << i for i, d in enumerate(reversed(bin(num)[2:])) if d == '1'}


def _two_powers_blhsing(n):
    m = 1
    while n >= m:
        if n & m:
            yield m
        m <<= 1


def two_powers_blhsing(n):
    return tuple(_two_powers_blhsing(n))


def two_powers_tsionyx(num):
    powers = []
    while num > 0:
        rest = num & (num - 1)
        powers.append(num - rest)
        num = rest

    return powers


funcs = [
    two_powers_op_____,
    two_powers_jdehesa,
    two_powers_matina_,
    two_powers_agile_e,
    two_powers_taras__,
    two_powers_sunitha,
    two_powers_blhsing,
    two_powers_tsionyx,
]


# ================== UTILITY FUNCTIONS ======================= #

def _partial_map(f, vals):
    """Run function on a range of inputs as a single function"""
    p = functools.partial(map, f, vals)
    p.__name__ = f.__name__
    return p


def _sanity_check(f, n):
    factors = f(n)
    assert len(factors) > 0
    # factors are unique
    assert len(set(factors)) == len(factors)
    assert sum(factors) == n

    for f in factors:
        b = bin(f)
        assert b == '0b1' + '0' * (len(b) - 3)


def benchmark(fs, inputs):
    for f in fs:
        for n in inputs:
            _sanity_check(f, n)

    aggr_funcs = [_partial_map(f, inputs) for f in fs]

    res = dict()
    print(inputs)
    for f in aggr_funcs:
        print(f.__name__, end=' ')
        tres = %timeit -o tuple(f())
        res[f.__name__] = tres.average

    return res


def plot(results):
    %matplotlib inline
    import matplotlib.pyplot as plt
    import matplotlib

    plt.figure(figsize=(10, 10))
    matplotlib.rcParams.update({'font.size': 18})

    leg = []
    for k, v in results.items():
        x, y = zip(*sorted(v.items()))
        plt.plot(x, [i * 10 ** 6 for i in y])
        leg.append(k)

    plt.legend(leg, loc='upper left')
    plt.ylabel('μs')
    plt.show()


full_res = defaultdict(dict)
for degree in range(2, 7):
    print('Benchmark <=10^%i' % degree)
    for name, t in benchmark(funcs, [random.randint(1, 10 ** degree) for _ in range(10)]).items():
        full_res[name][degree] = t

# you can view the results if you run it inside a jupyter notebook
# just uncomment the following line
# plot(full_res)

在Lenovo ThinkPad E480上测得

答案 5 :(得分:1)

>>> n=42
>>> {1<<i for i,d in enumerate(reversed(bin(n)[2:])) if d=='1'}
{8, 2, 32}

答案 6 :(得分:1)

您可以使用带有移位位掩码的生成器:

function  isLetter(value) {
   value = value.toUpperCase();
   for (let i = 0; i < value.length; i++) 
      if (!(value[i] >= "A" && value[i] <= "Z")) return false;

   return true;
}

因此:

def two_powers(n):
    m = 1
    while n >= m:
        if n & m:
            yield m
        m <<= 1

将是:

tuple(two_powers(42))

答案 7 :(得分:0)

受到Agile_Eagle's answer

的启发
def two_powers(num):
    powers = []
    while num > 0:
        rest = num & (num - 1)
        powers.append(num - rest)
        num = rest

    return powers