首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >美食挑战:可爱的幸运羔羊

美食挑战:可爱的幸运羔羊
EN

Code Review用户
提问于 2019-05-01 17:48:11
回答 2查看 1.7K关注 0票数 4

我已经能够把我的解决方案减少到固定的时间。是否有可能进一步将这一比率降低任何常数?(除了琐事,即插入常量,而不是延迟导入)。具体来说,如果有不使用日志的方法,我想加快fastMinPayout的速度。

可爱的幸运羔羊是一个追随者,并不是所有的苦差事。偶尔,当兰布达指挥官感到慷慨时,她会分发幸运羔羊(兰伯达的万能金钱雄鹿)。追随者可以用幸运的羊羔买第二双袜子,枕头作为他们的铺位,甚至第三天的饭!然而,实际上分发羊羔并不容易。每个追随者队伍都有一个严格的资历等级,这必须得到尊重--否则,这些追随者会反抗,你们都会再次被降职为奴才!为了避免叛乱,你必须遵守4条关键规则:

  1. 最低级的亲信(资历最少的)只得到一只羔羊。(一支球队中总会有至少一名追随者。)
  2. 如果排在他们前面的人得到的羔羊数量是他们的两倍以上,那么一个追随者就会反抗。
  3. 如果给下两个下属的羔羊加起来的数量比他们得到的羔羊的数量还要多的话,那么一个亲信就会反抗。(请注意,两个最低级的追随者不会有两个下属,所以这条规则不适用于他们。第二位最低级的追随者至少需要和最低级的亲信一样多的羔羊。)
  4. 你总能找到更多的佣人--指挥官有很多雇员。如果剩下足够多的羔羊,那么在遵守其他规则的同时,还可以增加另一个亲信为最高级的,那么你就必须不断地增加并付钱给那个亲信。

请注意,您可能无法分发所有的羔羊。一只羔羊不能再细分。也就是说,所有的追随者都必须得到一个正整数的羔羊数。编写一个名为“答案”( total_lambs )的函数,其中total_lambs是要除以的讲义中羊羔的整数。它应该返回一个整数,表示能够分享羊羔的最小和最大数目的区别(也就是说,尽可能慷慨地对待你付出的人和尽可能吝啬的人),同时仍然遵守上述所有规则以避免叛乱。例如,如果你有10只羔羊,并且尽可能慷慨的话,你只能支付3只追随者(1,2,4只羔羊,按等级排列),而如果你吝啬的话,你可以付给4名追随者(1,1,2和3只羔羊)。因此,答案(10 )应该返回4-3 =1以保持趣味性,Lambda指挥官改变了幸运羔羊支付的大小:您可以期望total_lambs总是在100亿到10亿之间( 10 ^ 9)。

代码语言:javascript
复制
''' Recursive form
Rules:
1) A0 = 1
2) An+1 !> 2*An
3) An-1 + An-2 !> An
4) n -> inf

Rewritten:
1) A0 = 1
2) An <= 2*An-1
3) An >= An-1 + An-2
4) n -> inf

Therefore:

A0 = 1, A-1 = 0, A-2 = 0
An-1 + An-2 <= An <= 2*An-1
'''


def maxPayout(LAMBs):
    '''
    Given An-1 + An-2 <= An <= 2*An-1
    An is maximized when An = 2*An-1
    '''
    # payouts[0] and payouts[1] exist as 'dummy' payouts
    payouts = [0, 0, 1]
    LAMBs -= payouts[-1]

    while (LAMBs >= 0):
        payouts.append(2*payouts[-1])
        LAMBs -= payouts[-1]

    # -2 for first two 'dummy' payouts and -1 for extra payout
    return len(payouts) - 2 - 1


def minPayout(LAMBs):
    '''
    Given An-1 + An-2 <= An <= 2*An-1
    An is minimized when An = An-1 + An-2
    '''
    # payouts[0] and payouts[1] exist as 'dummy' payouts
    payouts = [0, 0, 1]
    LAMBs -= payouts[-1]

    while (LAMBs >= 0):
        payouts.append(payouts[-1] + payouts[-2])
        LAMBs -= payouts[-1]

    # -2 for first two 'dummy' payouts and -1 for extra payout
    return len(payouts) - 2 - 1


def solution(total_lambs):
    return minPayout(total_lambs) - maxPayout(total_lambs)


def fastMaxPayout(LAMBs):
    '''
    Since maxPayout follows An = 2*An-1, maxPayout follows
    geometric sequence that can be reduced to exponential.

    An = 2^n

    Then we solve for sum(An = 2^n) <= LAMBs and the geometric
    series' sum formula follows:

    Sn = A0 * 1-r^n / (1-r)
    Sn = 1 * 1-2^n / (1-2)
    Sn = 2^n - 1

    Now we finally solve (Sn = 2^n - 1) <= LAMBs

    2^n <= LAMBs + 1
    n <= log2(LAMBs + 1)
    n = floor(log2(LAMBs + 1)) = (LAMBs + 1).bit_length() - 1
    '''
    return (LAMBs + 1).bit_length() - 1


def fastMinPayout(LAMBs):
    '''
    Since minPayout follows An = An-1 + An-2, minPayout follows
    a shifted Fibonnacci sequence. Apply Binet's formula to
    derive the nth Fibonnacci sequence.

    An-1 = Fn = ((1+sqrt(5) / 2)^n - (1-sqrt(5) / 2)^n) / sqrt(5)

    Substitute constants for variables to simplify

    let a = 1+sqrt(5) / 2
    let b = 1-sqrt(5) / 2
    let x = An-1 * sqrt(5)

    x = a^n - b^n
    a^n = x + b^n
    n = loga(x + b^n)ls

    And since lim n->inf b^n = 0, Binet's formula approximates:

    n = loga(x)

    Now we finally solve (Sn = An+2 - 1) >= LAMBs

    n+3 = loga(An+2 * sqrt(5)) = loga((An+2 - 1 + 1) * sqrt(5))
    n = loga((LAMBs + 1) * sqrt(5)) - 3
    '''
    from math import log, ceil, sqrt
    return int(ceil(log((LAMBs + 1 + 0.5)*sqrt(5), (1+sqrt(5)) / 2)) - 3)


def fastSolution(total_lambs):
    return fastMinPayout(total_lambs) - fastMaxPayout(total_lambs)


print(solution(143), fastSolution(143))
EN

回答 2

Code Review用户

发布于 2019-05-02 01:53:13

FP算术

除了琐事,比如插入常数

这是一个常量,但也许你会把它看作是非平凡的。首先,它允许将一个除法转化为(更便宜的)乘法。

与其向math.log()提供两个args,不如使用这种更快的技术:

代码语言:javascript
复制
    # one-time init
    rad_five = sqrt(5)
    phi = (1 + sqrt(5)) / 2
    recip_phi = 1 / phi

    # compute log base b, that is, base phi
    log((LAMBs + 1 + 0.5) * rad_five) * recip_phi

使用1 arg调用,然后再乘以肯定会跑得更快。

查找表

我们接受最多10亿的数字,然后计算大量详细的尾数位,结果ceil()丢弃了其中的大部分。也就是说,您真正关心的唯一输入值是那些将日志结果超过下一个整数格点的值。将这些值存储在有序查找表中。phi重复乘法会帮助你找到它们。

在运行时,使用查找表上的二进制搜索计算相关的日志结果。

是的,你是对的,你想把imports提升到最顶尖的位置.

票数 5
EN

Code Review用户

发布于 2019-05-02 08:16:58

PEP-8

  • Python约定对变量和函数名使用snake_case
  • 你在数值运算符周围的间距不一致

注释

你的评论很清楚,它们解释了为什么要做一些事情。

payouts list

这份清单是不必要的。您只使用它的最后一个元素及其长度。更好的做法是只使用变量,就像标准的斐波纳契生成器和itertools.count来跟踪支出的数量。

代码语言:javascript
复制
def max_payouts(lambs):
    payout = 1
    for i in count():
        if lambs <= 0:
            return i
        payout *= 2
        lambs -= payout


def min_payouts(lambs):
    a, b = 0, 1
    for i in count():
        if lambs <= 0:
            return i
        a, b = b, a + b
        lambs -= b
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/219515

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档