任务源自旧版的Brilliant数学讨论问题。2019-09-02我曾经发布过,可惜已经下线,幸活大喵做足备份。
该问题看似是概率问题,实则不然。
官方给出的解法透露出一个非常重要的数学思维方法:
数学语言 —— 为何以及如何构造一个函数 f(n);
运用恰当构造的函数为逻辑推理的起点;
Python随机模块模拟验证之;
先从关键词开始——常用英文短语:
consecutive:连续 如1,2,3,4
in the line-up:排成一列
“There's a randomly ordered line of 15 forks and 15 spoons on the counter. Chef Nidhi only needs 5 forks and 5 spoons.Can she always pick up exactly what she needs by selecting 10 consecutive utensils starting from somewhere in the line-up?
图片
15把叉子和15把勺子随机摆放的,图为示例
题目考察数学思维:
丁丁猫一家的厨房有30付餐具,包含15个叉子和15把刀。丁大喵说了,叉子和勺子的摆放要方便拿取,家里5个人吃饭,桌子上需要摆放5付刀叉,取餐具的时候,很方便地找出5把叉子和5把刀。
一家之主的丁老喵让孩子们想想,该怎么摆放?
丁小喵抢先发话:“每次刷碗后,叉子和勺子要分开摆放”
“不需要” 丁大喵说:"不必这么麻烦,经常有不自觉的取走不按规则放回去,
“ 无论刀叉怎样摆放顺序,都可以找到10个连续摆放的餐具,恰好满足有5个叉子和5个勺子!”
哦?真的可以做到吗?
丁大喵说的是真的吗?
任务场景之二
学校举办新学期的运动会,首先班级推荐10个旗手,要有5男生和5个女生组成方阵,走在班级队伍的最前面,要求10人的身高要尽量接近。
班里共有30人其中15个男生和15个女生。老师先按身高排出30人有高到低排出顺序。请问应该如何选出 5 对男女生推荐为旗手方阵的人选。
——似乎和刀叉的摆放有异曲同工之妙!
逻辑推理的思路梳理:
将30个餐具分成 20个连续的组。最左边开始选10个,即1到10为第1组,2-11为第2组,... 第20-30为第20组 —— 分组是整个思路的关键!!
设一个函数f(n)返回一组器具中叉的数量—— 数学语言描述
该函数显然是连续的,满足:
(1)因为两个连续组的叉的数目之间的差不能大于1。
考虑30把厨具里必然有15把叉子,那么下面必然成立
f(1)+f(10)+f(20) = 15
再看 f(1),f(10),f(20) 3个组中有没有等于5的,如果有,就是我们要找的那一组。如果没有,这三个函数的返回值必然要满足:
(2)至少一个必须大于5,其中一个小于5。
然后,如上所述函数返回值必然满足(1),(2),那么存在某个f(n),n不在1,10,20之间,但f(n)必然是小于5和大于5之间某个数,连续变化的数列中:
譬如3,4,5,6,7之间,可见,由于满足下面两个条件,必然存在某个n,f(n)的返回值恰好为5!
(1)两个连续组的叉的数目之间的差不能大于1
(2)至少一个必须大于5,其中一个小于5
丁小喵说:
“总觉的不踏实,能不能验证下呢?
怎么验证呢
... ...
丁大喵提醒小喵,你这学期学python了
试试python可以啊?!
编程时刻开始 ...
叉子表达为1,勺子表达为0
随机生成一个只有0,1的数列
... ...
import random
from collections import Counter
while True:
group = [random.randint(0, 1) for i in range(30)]
#叉子为1,勺子为0,随机数量生成数组group
num_fork = str(group).count("1")
num_spoon = str(group).count("0")
if num_fork == 15 and num_spoon ==15:
print(group)
print(Counter(group))
break
j = 0
while True:
#print('随机顺序摆放的叉子和勺子:',group[j:j+10])
fork = str((group[j:j+10])).count("1")
spoon = str(group[j:j+10]).count("0")
if fork == 5 and spoon == 5:
print ('j =',j)
print (group[j:j+10])
break
j += 1
生成刀叉的随机摆放顺序:
[1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
1, 1, 0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 1, 1, 1, 0, 0, 1, 0, 0]
保证刀叉各有15把:
Counter({1: 15, 0: 15})
j = 4
[0, 0, 1, 0, 0, 1, 1, 1, 0, 1]
解读:j =4 就是数列第5个数开始算起,10个连续厨具恰好满足5个叉子和5个勺子
时至今日,再看该任务依然启发力道十足!看能否再做些改进,也只能在随机生成函数的代码做些精简而已。
如何精简?
-
构造一个随机数列包含15个 1(folk)和 15个 0 (spoon) -
长度为 30 的数组,包含30个 0 -
随机生成 15个数为0-29之间的地址脚标
random_index = [random.randint(0,29) for _ in range(30)]
print('random_index =',len(random_index),random_index)
random_index = 30 [13, 22, 4, 21, 7, 27, 2, 1, 27, 6,
15, 20, 26, 22, 24, 29, 22, 29, 2, 4, 11, 2, 13, 27,
2, 25, 28, 10, 23, 0]
显然有重复的值,不可取。不过可以去重操作完善之。 数组求和结果为15则验证随机数组符合要求。
def randomIndex(n):
cunt,inbox = 0,[]
init = [0] * n
while sum(init) < n//2:
#循环终止条件是恰好一半的元素值为 1
idx = random.randint(0,29)
if idx not in inbox: #地址去重
inbox.append(idx)
init[idx] = 1
else:pass
cunt += 1
return init,sum(init)
n = 30
print(randomIndex(n))
([0, 1, 0, 1, 0, 0, 0, 0, 0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 0,
0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1], 15)
以上输出的随机数组满足要求!
def check(s,l):
for i in range(len(s)-l):
if s[i:i+l].count(0) == s[i:i+l].count(1):
return s[i:i+l],i
else:return None
s = [1, 1, 0, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 1, 1, 0, 1, 0, 0,
1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]
l = 10
print(check(seq,l))
([0, 1, 1, 0, 0, 1, 1, 0, 1, 0], 8)
第 8 个元素开始连续 10 个恰好包含 5个叉和5个勺。
课程设计:丁丁猫亲子创客
本节课两个任务场景激发编程的动力,适于小学6年级以上的孩子;
锻炼孩子遇到实际场景需求,先抽象为可用编程实现的思维过程;
熟悉并使用python数组的切片、数组元素统计函数使用;
使用英文描述和理解问题需求,掌握两个math里常见的单词 consecutive in the line-up
理解f(n)函数的本质,用函数描述问题的好习惯;
本文由 mdnice 多平台发布