codewars -- Count ones in a segment (python)

14 篇文章 0 订阅
13 篇文章 0 订阅

Count ones in a segment

难度系数: 4kyu
这个题本来算一个较简单的题目,但提交了几次都显示超时,因此肯定不能使用便利计算,应通过找规律求解!!!规律找了好久才发现,分享给大家。
以下给出最优解(通过,公式计算)和 超时解(未通过,遍历累计)!!!
题目:

Given two numbers: ‘left’ and ‘right’ (1 <= ‘left’ <= ‘right’ <= 200000000000000) return sum of all ‘1’ occurencies in binary representations of numbers between ‘left’ and ‘right’ (including both)

Example:

countOnes 4 7 should return 8, because:
4(dec) = 100(bin), which adds 1 to the result.
5(dec) = 101(bin), which adds 2 to the result.
6(dec) = 110(bin), which adds 2 to the result.
7(dec) = 111(bin), which adds 3 to the result.
So finally result equals 8.

1. 规律求解

通过陈列一部分1的个数可以发现有以下规律:
从0开始,1的个数变化为

[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,....]

可以发现
每2的次方区间内,都是累加之前区间,并对每个元素进行+1,以此类推:如下

[0,1] [1,2] [1,2,2,3],[1,2,2,3,2,3,3,4],...
--> [0,1] [0+1,1+1] [0+1,1+1,1+1,2+1],[0+1,1+1,1+1,2+1,1+1,2+1,2+1,3+1],...

因为

n = 2**p + 2**q +...+ 2**0

继续推理可以发现
要求当前数n的1的个数,只需分块求解即可,并累加当前数 n到求解区间的差值,因为相隔多远就得加多少次 +1。

因此转化为求解2的若干次方内1的个数的累加和

在2的1次方内,sum([0,1])=1 = 2**0
在2的平方内, sum([0,1,1,2]) = 4 = 2**1 + 2**0 + 2**0 = 2*(2**1)
在2的3次方内, sum([0,1,1,2,1,2,2,3]) = 12 
                                   = 2**2 + 2*2**1 + 4*2**0
                                   = 3*(2**2)

以此类推规律是不是出来了

在2的p次方内(不包括2**p这个数), sum = p*(2**(p-1))
因此还得加上2**p这个数的1的个数,为1
则有 sum = p*(2**(p-1))+1
又因为 2**p区间距离n的距离为 n-2**p
所以每个区间的累计值为 sum = p*(2**(p-1))+1 + (n-2**p)

接下来就是叠加每个区间的求解值!!!

完整代码为:

def countOnes(left, right): 
    return count(right) - count(left-1)
def count(n):
    s = 0
    while n:
        p = n.bit_length()-1 
        p2 = 1<<p
        n -=  p2
        s += p*(p2>>1)+1 + n
        print (p,s,n)
    return s

顺利通过,言简意赅!!!

一下为未通过,超时解也顺便贴出:

超时解法1:

def countOnes(left, right):
    sums = 0
    binary = ''
    for i in range(left,right+1):
        binary += bin(i)[2:]
    for j in binary:
        if j == '1':
            sums += 1
    return sums

超时解法2:

def countOnes(left, right):
    sums = 0
    for i in range(left,right+1):
        while i:
            if i%2:
                sums += 1
            i = i//2
    return sums
import time 

超时解法3:

def countOnes(left, right):
    r = right
    max_r = 0
    while right:
        max_r += 1
        right = right//2
    ini = [0,1]
    for i in range(max_r):
        ini += [x+1 for x in ini]
    sums = sum(ini[left:r+1])
    return sums
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值