吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1073|回复: 16
收起左侧

[Python 原创] 试讲从嵌套循环到回溯法,我体会到了他的本质

[复制链接]
xhtdtk 发表于 2024-5-19 18:35
一、前因

最近班上的学生(小学5、6年级)要参加python的白名单比赛,从练习的结果来看,学生对于后面几道答题一直心有余悸,算法实在是太难了。
对于递归法,他们可以胸有成竹。对于回溯法,他们只能留下难过的泪水,因此怎么让学生听懂回溯法是重中之重。
而B站上或其他文章中,对于回溯法的讲解总让我感觉朦朦胧胧,因此自己尝试解开回溯法的面纱。


二、先从嵌套循环求排列


对于固定数量的排列组合,如从'abc'中选2个进行排列,可以使用2个for循环来解决
[Python] 纯文本查看 复制代码
target = 'abc'
for x in target:
	for y in target:
		if x != y:
			print(x, y)

得出的结果为
[Python] 纯文本查看 复制代码
a b
a c
b a
b c
c a
c b


如从'abc'中选3个进行排列,可以使用3个for循环来解决
[Java] 纯文本查看 复制代码
target = 'abc'
for x in target:
	for y in target:
		for z in target:
			if x!=y and x!=z and y!=z:
				print(x, y, z)

得出的结果为
[Python] 纯文本查看 复制代码
a b c
a c b
b a c
b c a
c a b
c b a


三、思考


通过嵌套循环的确可以进行排列,但是我想从'abcdefg'中分别选3、4、5个进行排列,难道每次都要手动增加for循环次数吗?
而之前学习的递归法,函数通过自己调用自己好像可以帮助我解决这些问题,先尝试一下。


四、尝试使用递归法


我先创建一个方法,自己调用自己2次,然后遍历abc不就行了吗,并且使n传入的参数为n-1为嵌套循环
[Python] 纯文本查看 复制代码
def abc_arrange(n, data):
	# 遍历abc
	for x in data:
		print(x)

	# 调用自己2次
	for i in range(n):
		abc_arrange(n-1, data) # 传入的参数为n-1为嵌套循环

n = 2 # 选2个进行组合
target = 'abc' # 组合为abc
abc_arrange(n, target)

结果
[Python] 纯文本查看 复制代码
a
b
c
a
b
c
a
b
c
a
b
c
a
b
c


不行,思考一下嵌套循环里,大循环套着小小循环。
结果应该是:大循环的a对应小循环abc,大循环的b对应小循环abc,大循环的c对应小循环abc,那调用自身函数也是在循环里执行的,并且当n为0的时候,说明是最后一次嵌套循环,此时应该return
[Python] 纯文本查看 复制代码
def abc_arrange(n, data):
	if n == 0:
		return

	# 遍历abc
	for x in data:
		print(x)
		abc_arrange(n-1, data)

n = 2 # 选2个进行组合
target = 'abc' # 组合为abc
abc_arrange(n, target)

结果为
[Python] 纯文本查看 复制代码
a
a
b
c
b
a
b
c
c
a
b
c


这次好像可以了,请看下图
微信图片_20240519175241.png

现在只要想办法将大循环的abc与每个小循环的abc进行合并,就能得到aa,ab,ac,ba,bb,bc,ca,cb,cc
那就创建一个空列表为combination传入函数,用来存储合并结果
[Python] 纯文本查看 复制代码
def abc_arrange(n, target, combination):
	if n == 0:
		print(combination) # 打印合并结果
		return

	# 遍历abc
	for x in target:
		combination.append(x) # 合并结果
		abc_arrange(n-1, target, combination)

n = 2 # 选2个进行组合
target = 'abc' # 组合为abc
combination = []
abc_arrange(n, target, combination)

结果为
[Python] 纯文本查看 复制代码
['a', 'a']
['a', 'a', 'b']
['a', 'a', 'b', 'c']
['a', 'a', 'b', 'c', 'b', 'a']
['a', 'a', 'b', 'c', 'b', 'a', 'b']
['a', 'a', 'b', 'c', 'b', 'a', 'b', 'c']
['a', 'a', 'b', 'c', 'b', 'a', 'b', 'c', 'c', 'a']
['a', 'a', 'b', 'c', 'b', 'a', 'b', 'c', 'c', 'a', 'b']
['a', 'a', 'b', 'c', 'b', 'a', 'b', 'c', 'c', 'a', 'b', 'c']


还不可以,一、元素重复了,二、合并结果长度不是2,再改一改
判断元素是否重复,重复就跳过
[Python] 纯文本查看 复制代码
def abc_arrange(n, target, combination):
	if n == 0:
		print(combination) # 打印合并结果
		return

	# 遍历abc
	for x in target:
		if x in combination: # 如果已经存在元素就跳过
			continue
		combination.append(x) # 合并结果
		abc_arrange(n-1, target, combination)

n = 2 # 选2个进行组合
target = 'abc' # 组合为abc
combination = []
abc_arrange(n, target, combination)

结果为
[Python] 纯文本查看 复制代码
['a', 'b']
['a', 'b', 'c']


问题又出现了,大循环和小循环的abc都合并在一起了,导致conbination的列表结果只能是abc
想一想嵌套循环,大循环为a,小循环abc,已经排除重复出现的元素了,结果应该为ab,ac
说明进入小循环后,应该把combination里的小循环得到的元素删除,才能进入小循环中的下一次循环
同样的进入大循环中进入下一次循环后,也要把combination里的大循环得到的元素删除,才能进入大循环中的下一次循环
而在这个方法中,我在for循环和递归实现了嵌套循环,拿在循环后把conbianation删除最后一个元素不就可以了
这就是回溯的本质吧
[Python] 纯文本查看 复制代码
def abc_arrange(n, target, combination):
	if n == 0:
		print(combination) # 打印合并结果
		return

	# 遍历abc
	for x in target:
		if x in combination: # 如果已经存在元素就跳过
			continue
		combination.append(x) # 合并结果
		abc_arrange(n-1, target, combination)
		combination.pop() # 在循环中进入下轮循环前删除最后一个元素

n = 2 # 选2个进行组合
target = 'abc' # 组合为abc
combination = []
abc_arrange(n, target, combination)

结果为
[Asm] 纯文本查看 复制代码
['a', 'b']
['a', 'c']
['b', 'a']
['b', 'c']
['c', 'a']
['c', 'b']


排列成功了,用一个result的列表来存储所有的combination,储存需要添加combination的副本,和全局变量有关系
[Python] 纯文本查看 复制代码
def abc_arrange(n, target, combination):
	if n == 0:
		print('combination', combination) # 打印合并结果
		result.append(combination.copy()) # 要添加副本,不能直接添加combination
		return

	# 遍历abc
	for x in target:
		if x in combination: # 如果已经存在元素就跳过
			continue
		combination.append(x) # 合并结果
		abc_arrange(n-1, target, combination)
		combination.pop() # 在循环中进入下轮循环前删除最后一个元素

n = 2 # 选2个进行组合
target = 'abc' # 组合为abc
combination = [] # 储存合并结果
result = [] # 储存所有的合并结果
abc_arrange(n, target, combination)
print('result', result) # 打印所有的合并结果

结果为
[Python] 纯文本查看 复制代码
combination ['a', 'b']
combination ['a', 'c']
combination ['b', 'a']
combination ['b', 'c']
combination ['c', 'a']
combination ['c', 'b']
result [['a', 'b'], ['a', 'c'], ['b', 'a'], ['b', 'c'], ['c', 'a'], ['c', 'b']]


成功了!试一下将排列换成组合,用集合排除重复的元素就可以了
[Python] 纯文本查看 复制代码
def abc_arrange(n, target, combination):
	if n == 0:
		if not set(combination) in result:
			print('combination', combination) # 打印合并结果
			result.append(set(combination.copy())) # 要添加副本,不能直接添加combination
		return

	# 遍历abc
	for x in target:
		if x in combination: # 如果已经存在元素就跳过
			continue
		combination.append(x) # 合并结果
		abc_arrange(n-1, target, combination)
		combination.pop() # 在循环中进入下轮循环前删除最后一个元素

n = 2 # 选2个进行组合
target = 'abc' # 组合为abc
combination = [] # 储存合并结果
result = [] # 储存所有的合并结果
abc_arrange(n, target, combination)
print('result', result) # 打印所有的合并结果

结果为
[Python] 纯文本查看 复制代码
combination ['a', 'b']
combination ['a', 'c']
combination ['b', 'c']
result [{'a', 'b'}, {'a', 'c'}, {'c', 'b'}]


感想


直接教我回溯法,教的我都懵了,果然从嵌套循环入手领悟到了回溯的本质,深度路径搜索算法和广度路径搜索算法都比这个容易理解

免费评分

参与人数 2吾爱币 +9 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Arcticlyc + 2 真羡慕,现在小学就学编程了

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

jamesAbc 发表于 2024-5-20 09:46
我的理解:回溯的本质其实还是暴力枚举+递归,只不过有些题目或场景无法确定循环次数,所以只能用回溯让计算机帮我们“暴力枚举”

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
xhtdtk + 1 + 1 我很赞同!

查看全部评分

niudaiche 发表于 2024-5-20 20:27
最后处理无序组合存在逻辑错误:集合set不能被直接放入另一个列表list中因为集合是不可哈希的,需要用到元组,其他都对

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
xhtdtk + 1 + 1 热心回复!

查看全部评分

你好,再见 发表于 2024-5-19 20:09
6,小学就要考dfs了
搜了一下python有提供全排列函数,可以直接调用
头像被屏蔽
hjsen 发表于 2024-5-19 20:28
提示: 作者被禁止或删除 内容自动屏蔽
foolishman1983 发表于 2024-5-19 20:37
学习了,
 楼主| xhtdtk 发表于 2024-5-19 20:43
你好,再见 发表于 2024-5-19 20:09
6,小学就要考dfs了
搜了一下python有提供全排列函数,可以直接调用

但是比赛是在网页上考,以后学c++也要用
无闻无问 发表于 2024-5-19 20:43
itertools库,自带
willgoon 发表于 2024-5-20 00:16
小学也这样卷了吗?
jm1jm1 发表于 2024-5-20 06:57
很好的一个软件,谢谢分享
shiqiangge 发表于 2024-5-20 08:28
再次被小学生打败系列.......
Kls673M 发表于 2024-5-20 09:06
这么卷吗,

6年级就开始搞这个了,
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 12:30

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表