Ruby实现3x3数独生成遇阻:uniq方法多条件过滤问题
解决Ruby中筛选3元素循环排列的问题
看起来你混淆了uniq方法的用法——uniq是用来去重(根据块返回的键值合并重复项),而不是用来筛选符合条件的元素的。你写的uniq{ _1.index(1) && _1.index(2) && _1.index(3) }其实每个排列都会返回true(因为所有排列都包含1、2、3),所以uniq会认为所有元素的键都是true,最终只保留第一个排列,这显然不是你想要的结果。
你的需求分析
你想要的是3元素的循环移位排列:比如[1,2,3]、[2,3,1]、[3,1,2],这些排列是通过将基础排列循环左移/右移得到的。如果是要从所有排列中筛选出这类结果,或者直接生成它们,有两种简单的方法:
方法1:直接生成循环移位排列
如果你已经确定基础排列(比如[1,2,3]),可以用rotate方法直接生成所有循环移位结果:
base = [1, 2, 3] cyclic_perms = 3.times.map { |i| base.rotate(i) } # 输出:[[1, 2, 3], [2, 3, 1], [3, 1, 2]]
rotate(i)会把数组向左旋转i位,执行3次就能得到所有循环移位的排列。
方法2:从全排列中筛选符合条件的项
如果你需要从所有6种排列中筛选出循环移位的那些,可以用select方法(这才是用来过滤条件的正确方法),判断每个排列是否是基础排列的循环移位:
all_perms = (1..3).to_a.permutation.to_a base = [1, 2, 3] valid_perms = all_perms.select do |perm| # 检查当前排列是否是base的任意次数循环移位 3.times.any? { |i| perm == base.rotate(i) } end # 输出:[[1, 2, 3], [2, 3, 1], [3, 1, 2]]
为什么你的uniq用法不对?
再强调一下:uniq的块是用来生成一个标识键,当两个元素的键相同时,uniq会只保留第一个。比如如果你想把循环移位的排列归为一组并只保留一个,才会用到uniq,比如:
all_perms = (1..3).to_a.permutation.to_a # 用循环移位的基础形式作为键,实现分组去重 unique_cyclic_groups = all_perms.uniq do |perm| # 找到排列中最小元素的位置,将数组旋转到以最小元素开头的形式作为键 min_idx = perm.index(perm.min) perm.rotate(min_idx) end # 输出:[[1, 2, 3], [1, 3, 2]]
这时候uniq会把循环移位的排列视为同一组,每组保留第一个元素,得到两个不同的循环组。
内容的提问来源于stack exchange,提问作者vrintle
相关产品推荐
相关产品推荐

