韩信分油、泊松分酒、别莱利曼分水等问题的通用解法

问题描述:

初等数学中的分油(酒/水)问题,在世界各地有多个版本:

  • 韩信遇到两个路人争执不下,原因是两人有装满10斤的油篓和两个3斤、7斤的空油篓,无法平均分出两份,每份5斤油。韩信是如何解决这个难题的?
  • 某人有12品脱美酒,想把一半赠人,但只有一个8品脱和一个5品脱的容器,问怎样才能把6品脱的酒倒入8品脱的容器中。
  • 一只水桶可容12杓水,还有两只空桶,一只容量9杓,另一只5杓,怎样利用这两只空桶来把这大水桶中满盛的水分做两半?
  • 有装有14千克酒的容器,另外有可装5千克和9千克酒的容器,要把酒平分,该如何办?
  • 有一个装满油的8公升容器,另有一个5公升及3公升的空容器各一个,且三个容器都没有刻度,试将此8公升油分成4公升。

思路分析:

1. 均分原理:如果两个数互质,那么只用这两个数相加减,就可以求出所有自然数。

2. 分油(酒/水)问题可以抽象如下:假设有3个容器A、B、C,A装满液体,_{}V_{A}>V_{B}>V_{C}V_{A}\leqslantV_{B} + V_{C}V_{A}为偶数,V_{B}V_{C}为互质数的倍数。要求利用3个容器将A中液体均分成两等份。

3. 倾倒顺序共有P_{3}^{2}=6种,在这里我们只考虑往一个方向倾倒(单向闭环),因此共有两种方法:

方法一:从A到B、从B到C、从C到A,其中A到B不能倒空、C到A必倒空、B到C分倒空与不倒空两种情况。

方法二:从A到C、从C到B、从B到A,其中A到C不能倒空、B到A必倒空、C到B分倒空与不倒空两种情况。

4. 倾倒条件:满足上述倾倒顺序且倒完后不与已有倾倒状态重复。四种倾倒情况只需也只能满足其一,若都不满足,该方法无法均分。

注意:如遇小数情况可转换为整数来解决(比如4,2.5,1.5可转换为8,5,3)。

参考代码:

def divide(m, left, middle, right):
    """均分过程"""
    state, n = [(left,0,0)], 0  #初始化二维列表、索引

    #循环结束条件:左容器与中容器中装入均分后的液体(方法一)或者左容器与右容器中装入均分后的液体(方法二)
    while (state[n][0] != left / 2 or state[n][1] != left / 2) and (state[n][0] != left / 2 or state[n][2] != left / 2):

        #新一轮倾倒前两个容器中液体的叠加状态
        i = state[n][0] + state[n][1]
        j = state[n][1] + state[n][2]
        k = state[n][0] + state[n][2]

        #从左到中,左不倒空,中倒满
        if i > middle and (i - middle, middle, state[n][2]) not in state:
            state.append((i - middle, middle, state[n][2]))
        #从中到右,中不倒空,右倒满
        elif j > right and (state[n][0], j - right, right) not in state:
            state.append((state[n][0], j - right, right))
        #从中到右,中倒空
        elif j <= right and (state[n][0], 0, j) not in state:
            state.append((state[n][0], 0, j))
        #从右到左,右倒空
        elif (k, state[n][1], 0) not in state:
            state.append((k, state[n][1], 0))
        else:  #如果未执行过上述四种情况,循环结束
            print('\n采用方法%s,无法均分!' %m)
            return
        n += 1

    print('\n采用方法%s,总共需要倾倒%d次,具体过程如下:\n' %(m, n))
    print('容   积: %3d %3d %3d' %(left, middle, right))
    for num, group in enumerate(state):
        print('第%03d次:' %num, end = ' ')
        for each in group:
            print('%3d' %each, end = ' ')
        print()

if __name__ == '__main__':
    """主程序"""
    A, B, C = map(int, input("请按从大到小顺序输入三个容器容积(以空格分开):").split())  
    divide('一', A, B, C)  #方法一
    divide('二', A, C, B)  #方法二

运行结果:

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值