数字轮盘最大值问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TuringGo/article/details/83145117

问题描述

轮盘游戏来自赌场,轮盘上面平均分成若干个小的扇形,每个扇形上都有一个数字,流传较广的轮盘数字布局有“欧式规则”和“美式规则”两种。当 2 <= n <= 36时,求连续 n 个数字的和最大值的数字排列情况,以及在求出轮盘连续 n 个数字最大值情况下,欧式轮盘规则下的和小于美式轮盘规则下的和的情况个数?

欧式规则数字排列: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 = 3 时,对于欧式规则下的数字组合有(0,32,15),(32,15,19),…,(26,0,32)这些可能,所有这些组合中,和的最大组合是(36,11,30)这个组合,和为:77;对于美式规则下的数字组合有(0,28,9),…,(2,0,28)这些可能,所有这些组合中,和最大的组合是(24,36,13),和为:73

分析

  • 如题,这是一个数字圆盘,也就是说如果用一维数组来表示的话,数组的最后一个元素要与数组的第一个元素相邻,其实就是环形队列数据结构。
  • 每次取 n 个数字,对于从当前 index 索引开始的数字组合而言,只要 index + n - 1 的结果不超过数组的最大索引值,则只需要将 array[ index, index + n - 1] 之间的所有数字相加即可;若 index + n - 1的结果大于数组的最大索引值,那么由于数字轮盘是一个环形队列,因此剩余的元素,继续从数组的第一个元素开始数,直到取足 n 个数字,然后将这 n 个数字相加。
  • 对于求 m 组数组中连续的 n 个数字而言,有两种策略,一种是每次从一个索引位置开始,然后取 n 个数字求和;另一种策略是,设计一个长度为 n 的数字队列,队首是第一索引位置 index,队尾是索引位置 index + n - 1,先计算第一个队列的数字和,接下来每次对队首出队一个元素,队尾入队一个元素,直到队首元素是数组的最后一个元素出队时,意味着所有的组合结束
  • 第二种策略的好处就在于,它避免了大量的重复计算,除了第一次需要对队列中所有元素求和外,剩余的所有数字组合队列,只需要用上一个数字队列的和,加一个下一个元素(队尾入队),减去一个数字队列中索引最小的元素(队首出队)。而第一种策略每次都会计算长度为 n 的队列数字,相邻两个数字队列存在 n - 1 个元素都是被重复计算了的,效率不好。

算法

  • 两层循环,外层循环设置 n 的取值范围,内层循环设置索引的取值范围。这里虽然欧式轮盘的数字个数是 37 个,美式轮盘的数字个数是 38 个,但是由于 n 的最大长度为 36 ,两者又需要求和作比较,所以对于 38 个数字的美式轮盘而言,要处理的其实跟 37 个数字的欧式轮盘是一样的次数和索引相同。
  • 函数每次返回,从索引 0 开始直到最后一个索引位置的,指定长度为 n 的数字组合相加的和。这里使用列表来实现数字队列,用Python中列表的切片操作很容易求出指定范围内的数字之和。
  • 用两个列表来存储每次长度为 n 时,所有可能数字组合的和,分别求出两个数字轮盘的,长度为 n 的数字组合的和中的最大值。符合规则,则计算加一
  • 当计算完长度为 n 的这一轮时,清空刚才的列表,准备下一轮 n 的计算

Python代码实现

def loop_queue(index, n, queue):
    """
    :param index: 第一个元素的索引位置
    :param n: 当前数字组合队列的长度
    :param queue: 当前循环队列的类型
    :return: 队列数字之和
    """
    # 如果数字组合队列的长度等于队列的长度,则每个数字组合的结果都是队列所有元素之和
    if n == len(queue):
        return sum(queue)
     # 如果数字组合队列最大的索引位置,没有超过队列的最大索引时,直接切片计算即可
    elif len(queue) - index >= n:
        return sum(queue[index:index+n])
    # 如果数字组合队列的最大索引位置,超过队列的最大索引时,需要分两步切片计算
    else:
        return sum(queue[index:]) + sum(queue[:n-(len(queue)-index)])

# 欧式规则
european = [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]
# 美式规则
american = [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]
result1 = []
result2 = []
count = 0
for n in range(2, 37):
    for index in range(37):
        result1.append(loop_queue(index, n, european))
        result2.append(loop_queue(index, n, american))
    if max(result1) < max(result2):
        print(n, max(result1), max(result2))
        count += 1
    result1.clear()
    result2.clear()
print(count)

# 2 51 60
# 4 90 93
# 6 133 134
# 10 207 208
# 12 240 241
# 14 276 282
# 16 318 319
# 17 328 332
# 35 651 652
# 9

算法的优化

由上面分析可知,如果编程语言没有提供类似于Python中这么方便的切片功能,那么每次求 n 个长度的数字时,就需要依次计算,相邻两组数字之间就存在 n - 1 个数字被重复计算了。

当需要求的 n 的数字特别大时(事实上,n 最大值取决于 m 的长度),m 特别长时,就会存在大量的重复计算,性能不好。

因此下面给出了C语言实现的算法,改进之处在于对于每一轮 n 的计算只需要求出 m 次加法运算即可;不需要像之前的算法一样,每轮 n 的计算,需要计算 m * n 次加法运算。

C语言代码实现

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/TuringGo/article/details/83145117