程序员的算法趣题Q36: 翻转骰子

目录

1. 问题描述

2. 解题分析

3. 代码及测试

4. 后记


1. 问题描述

**越来越懒了。之前总是把题目描述敲键盘敲进去,太费劲了,碰上图的话就更痛苦。 拍个照直接贴上去就简单多了^-^.

2. 解题分析

        6个骰子构成的排列构成的总的状态数为6的6次方即6^6=46656种。

        从任意的状态出发经过翻转骰子的操作更新状态,其历经的状态序列都必然是如下图所示:

        其中上边代表一般性情况,下边可以看作是一种特殊情况。

        为什么必然会成为上图这种情况呢?根本原因在于这是一个有限状态序列(如前所述最多只有46656种状态)。从任意状态出发,最多经过46656步以后,必然会回到前面已经经过的某个状态(根据组合计数中的“鸽笼原则”或者“抽屉原则”)。这个洞见(Insight)是解决本题的关键。

        用acyclic存储已经判明的属于非循环的状态(即从它自己出发不会回到自身),用cyclic存储已经判明的属于循环的状态(即从它自己出发会回到自身)。

        从任意状态出发进行翻转更新,将历经的状态序列存储在statelst中:

        途中(包括一开始它自身)不管是碰到已判明是acyclic的还是cyclic的状态,都可以判断从出发状态到此刻为止的状态都是属于acyclic(为什么?请大家仔细品一品),将它们加入到acyclic

        如果途中某个状态已经存在于statelst(即上图所示情况),则如上图所示这表明在状态序列中到该状态之前的所有的状态都属于acyclic,其后(包括它自己)都属于cyclic,将这些状态分别加入acyclic和cyclic

        对所有46656种状态进行遍历(分别以它们为起始状态)重复以上过程即可。

        Acyclic和cyclic存储已经判明的结果,是为了避免重复搜索。毕竟,比如说,一旦你从某个状态出发已经找到了一个loop,那这个loop中的所有的状态都属于循环状态了,自然不必再针对其中每个状态重新搜索一次。

 

3. 代码及测试

# -*- coding: utf-8 -*-
"""
Created on Thu Sep 23 07:49:36 2021

@author: chenxy
"""

import sys
import time
import datetime
import random
from   typing import List
from   collections import deque
import itertools as it
import numpy as np

def getnext(cur:tuple)->tuple:
    cur_list = list(cur)
    # print(cur_list)
    c0 = cur[0]
    for k in range(c0):
        cur_list[k] = 7-cur_list[k]
    return tuple(cur_list[c0:] + cur_list[0:c0])

# print(getnext((1,1,6,1,6,1)))

tStart  = time.perf_counter()
cyclic   = set()
acyclic  = set()
for state in it.product([1,2,3,4,5,6],repeat=6):
    # print(state)
    cur = state
    statelst = []
    while 1:
        if (cur in acyclic) or (cur in cyclic):
            for s in statelst:
                acyclic.add(s)
            break
        if cur in statelst:
            beforeCur = True
            while len(statelst) > 0:
                s = statelst.pop(0)
                if s == cur:
                    beforeCur = False
                if beforeCur:
                    acyclic.add(s)
                else:
                    cyclic.add(s)
            break
        statelst.append(cur)
        cur = tuple(getnext(list(cur)))
        # print(cur)

tCost  = time.perf_counter() - tStart

print('total_count = {0}, tCost = {1:6.3f}(sec)'.format(len(acyclic),tCost))                  

        运行结果:total_count = 28908, tCost =  0.096(sec)

4. 后记

        这是到目前为止最令自己满意的一道题目。

        

        上一篇:Q35: 0和7的回文数

        下一篇:Q37: 翻转7段码

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

假设变刚度阻尼双足机器人动力学方程可以表示为: M(q)q'' + C(q, q')q' + K(q)q = f 其中,q是机器人的广义坐标向量,M(q)是质量矩阵,C(q, q')是科里奥利力矩阵,K(q)是刚度矩阵,f是外部力矩向量。 假设我们要求解某个参数p对所有变量的偏导数,那么我们需要先通过ode45求解机器人的运动学方程,得到q和q'随时间的变化情况。然后,我们可以利用MATLAB的符号计算工具箱,对动力学方程进行符号化处理,并求出所有变量的偏导数。具体步骤如下: 1. 定义符号变量 syms q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 q16 q17 q18 q19 q20 q21 q22 q23 q24 q25 q26 q27 q28 q29 q30 q31 q32 q33 q34 q35 q36 q37 q38 q39 q40 q41 q42 q43 q44 q45 q46 q47 q48 q49 q50 p 其中,q1~q50表示机器人的广义坐标,p是我们要求导的参数。 2. 符号化动力学方程 假设我们已经定义好机器人的质量矩阵M,科里奥利力矩阵C,刚度矩阵K和外部力矩向量f,那么我们可以通过如下代码符号化动力学方程: q = [q1; q2; q3; q4; q5; q6; q7; q8; q9; q10; q11; q12; q13; q14; q15; q16; q17; q18; q19; q20; q21; q22; q23; q24; q25; q26; q27; q28; q29; q30; q31; q32; q33; q34; q35; q36; q37; q38; q39; q40; q41; q42; q43; q44; q45; q46; q47; q48; q49; q50]; q_dot = diff(q); q_ddot = diff(q_dot); M = % 定义质量矩阵 C = % 定义科里奥利力矩阵 K = % 定义刚度矩阵 f = % 定义外部力矩向量 D = M*q_ddot + C*q_dot + K*q - f; 3. 求导 接下来,我们可以利用MATLAB的符号计算工具箱对动力学方程进行求导: dD_dp = diff(D, p); 4. 数值化 最后,我们可以将变量q和q'的数值代入到偏导数表达式中,得到p对所有变量的偏导数值: q_val = % 机器人广义坐标向量随时间的变化 q_dot_val = % 机器人广义速度向量随时间的变化 dD_dp_val = double(subs(dD_dp, [q; q_dot], [q_val; q_dot_val])); 其中,subs函数可以将符号变量中的所有数值替换为实际的数值,double函数可以将符号变量转换为双精度数值。最终,dD_dp_val将是一个与q和q'相同维度的向量或矩阵,表示p对所有变量的偏导数值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

笨牛慢耕

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

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

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

打赏作者

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

抵扣说明:

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

余额充值