程序员的算法趣题Q10: 轮盘的最大值

本文介绍了轮盘游戏中欧式和美式规则的数字排列,探讨了如何找到连续n个数字之和最大的情况。通过求移动平均的算法策略,实现了从两种规则下的数字序列中寻找最大连续和。最后,对于2到36之间的所有n,计算了欧式规则下的最大和小于美式规则下的最大和的n值,并给出了这些n的列表。
摘要由CSDN通过智能技术生成

目录

1. 问题描述

2. 解题分析

3. 代码及测试


1. 问题描述

        轮盘游戏被称为“赌场女王”。流传较广的轮盘数字排布和设计有“欧式规则”和“美式规则”两种。两种轮盘上的数字序列分别如下所示:

        欧式规则:
0,32,15,19,4,21,2,25,17,34,6,27,13,36,11,30,8,23,10,5,24,16,33,1,20,14,31,9,22,18,29,7,28,12,35,3,26

        美式规则:
0,28,9,26,30,11,7,20,32,17,5,22,34,15,3,24,36,13,1,00,27,10,25,29,12,8,19,31,18,6,21,33,16,4,23,35,14,2

        下图是欧式规则的轮盘示意图(侵删)。

        下面我们找到这些规则下“连续n个数字之和”最大的位置。举个例子,当n=3时,按照欧式规则得到的最大的组合是36,11,30这个组合,和为77;而美式规则下则是24,36,13这个组合,得到的和为73.(图中所示轮盘为欧式规则的轮盘,注意轮盘是圆形的)

        问题:当 2 ≤ n ≤ 36时,求连续n个数之和最大的情况,并找出满足条件“欧式规则下的最大和小于美式规则下的最大和”的n的个数。

2. 解题分析

        求连续n个数字的平均(本题是求和,但是求和与求平均只差一个平均系数,求平均也是也先求和再除以参与求和的数据的个数)在信号处理领域中称为求移动平均(moving average, or running average),其计算方式有一个小小的trick。令数据序列用x[k]表示,则从第m数开始的连续n个数的累加和的计算可以表示如下:

        这样就得到了一个递推关系,从上一个连续和sum[m-1]开始,只要把减去最前面一个数,加上后面一个数就可以得到新的sum[m]。这样可以极大地降低运算复杂度,在信号处理领域是常用技巧。

       本题还有另外一个需要注意的要点就是轮盘是圆形的,用线性数组表示轮盘上的数字排列数组的话,一部分的连续和涉及到头上一部分数字和尾巴上一部分数字的求和。最基本的做法就是用地址对数字长度进行求模运算。也可以用对数组进行线性扩展的方法以避免地址求模的处理。由于这个问题比较简单,本题解就用“简单粗暴”的前者对付一下了。

3. 代码及测试

# -*- coding: utf-8 -*-
"""
Created on Sun Aug 29 07:14:15 2021

@author: chenxy
"""

import sys
import time
import datetime
# import random
from   typing import List
# from   queue import Queue
# from   collections import deque

class Solution:

    euroRule= [0,32,15,19,4,21,2,25,17,34,6,27,13,36,11,30,8,23,10,5,24,16,33,1,20,14,31,9,22,18,29,7,28,12,35,3,26]
    usaRule = [0,28,9,26,30,11,7,20,32,17,5,22,34,15,3,24,36,13,1,0,27,10,25,29,12,8,19,31,18,6,21,33,16,4,23,35,14,2]
    
    def maxSum(self, nums:List, n:int) -> int:
        runningSum = sum(nums[0:n]) # The sum of the first n numbers as initial value
        sumMax     = runningSum

        M = len(nums)
        for k in range(1,len(nums)):
            runningSum += nums[(k+n-1)%M] - nums[k-1]
            if sumMax < runningSum:
                sumMax = runningSum
            # print('k ={0},runningSum={1},sumMax={2}'.format(k,runningSum,sumMax))        
        return sumMax
    
    def maxCoronaSum(self, nMin:int, nMax:int) -> List:
        """
        :
        :ret:   The list of numbers satisfying the condition.
        """                
        ans     = []
        
        for n in range(nMin, nMax+1):
            euroMax = self.maxSum(self.euroRule,n)
            usaMax  = self.maxSum(self.usaRule,n)
            # print('n ={0},euroMax={1},usaMax={2}'.format(n,euroMax,usaMax))
                
            if euroMax < usaMax:
                print('n={0}, euroMax={1}, usaSum={2}'.format(n,euroMax,usaMax))
                ans.append(n)
        
        return ans
                
if __name__ == '__main__':        
            
    sln    = Solution()    

    tStart = time.time()
    ans    = sln.maxCoronaSum(2,36)
    tCost  = time.time() - tStart
    print('nums ={0}, tCost = {1:6.3f}(sec)'.format(len(ans),tCost))        
    print('ans  ={0}'.format(ans))   

        运行结果:

       上一篇:Q09: 落单的男女

        下一篇:Q11: 斐波那契数列

        本系列总目录参见:程序员的算法趣题:详细分析和Python全解

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值