Algorithm 在不使用数组的情况下递归创建所有子集

Algorithm 在不使用数组的情况下递归创建所有子集,algorithm,recursion,subset,powerset,Algorithm,Recursion,Subset,Powerset,我们从用户那里得到非负整数n,我们必须打印集({1,2,3,…,n})的所有子集。(n循环的替代方法是递归 为了解决这个问题(我想……还没有测试过),我通过将样本日期制成表格来调查这个问题,并识别出三种状态,大小,开始,和限制,其进展如下: Size Start Limit Output 10 1 10 1..10 10 1 9 1..9 ... ... 10 1 1

我们从用户那里得到非负整数
n
,我们必须打印集
({1,2,3,…,n})的所有子集
。(
n循环的替代方法是递归

为了解决这个问题(我想……还没有测试过),我通过将样本日期制成表格来调查这个问题,并识别出三种状态,
大小
开始
,和
限制
,其进展如下:

Size  Start Limit   Output
  10      1    10    1..10
  10      1     9     1..9
              ...      ...
  10      1     1        1
  10      2    10    2..10
  10      2     9     2..9
              ...      ...
  10      2     2        2
        ...   ...      ...
  10     10    10       10
以下伪代码中的递归算法可以实现此目的:

printAllSets size
  printRows size 1

printRows size start
  print "{"
  printRow start size
  print "}"
  print CRLF
  if start <= size
    printRows size (start + 1)

printRow start limit
  if start <= limit
    print start + SPACE
    printRow start (limit - 1)
printAllSets大小
打印行大小1
打印行大小开始
打印“{”
打印行开始大小
打印“}”
打印CRLF

如果开始< P >递归算法是内存密集型的。这里的算法<代码> n> p>我想我们可以迭代地解决这个问题,我们可以假设它也可以被转换成递归,尽管它似乎是不必要的。考虑到我们可以使用常识来对给定的索引的任何组合进行排序。所以我们所需要做的就是数数。我们跳过的早期组合,以及在迭代的每个阶段需要解开多少组合(我可能在下面遗漏了一些东西,但我认为总体思路是正确的):


根据定义,集合
k
powerset k
的powerset是所有可能集合的集合,其中包含给定集合中的元素,包括空集本身。显然,当
k
是空集
powerset[]
时,就是包含空集的集合,
[[]
。现在,给定一组
k
powerset k
,则
k
的powerset加上一个附加元素
E
powerset(k+E)
,将包括所有可能包含不包含
E
powerset k
的元素的集合,以及那些相同的元素,除了现在所有包含
E

伪代码

let powerset k =
   match k with
   | [] -> [[]]
   | x:xs -> x * powerset xs + powerset xs
或具有同等性能的尾部调用

let powerset k =
   let (*) e ess = map (es -> e::es) ess
   reduce (e ess -> (e * ess) ++ ess) [ [] ] (reverse k)
..(以F#表示)

或者更简洁地说

let rec powerset = function
  | []    -> [ [] ]
  | x::xs -> List.append (List.map (fun es -> x::es) (powerset xs)) (powerset xs)
更好的版本将允许尾部调用优化…这是我们使用常见功能模式实现的:

let rec powerset2 k = 
  let inline (++) a b = List.concat [a;b]
  let inline (+*) a bs = List.map (fun b -> a::b) bs
  List.fold 
    (fun ac a -> (a +* ac) ++ ac)
    [ [] ] 
    (List.rev k)

--这一切都花了我一段时间才重新发现。这是一个有趣的小谜题。

提示:打印长度为零的所有子集,然后打印长度为1的所有子集,等等。直到最后打印长度为n的唯一子集。@thebjorn我不知道怎么做。我必须补充一点,子集的序列必须与示例完全相同。首先是指具有1的子集,然后是具有2和….的子集。。。。必须首先打印最长的子集。不仅仅是长度为零。那么长度1和…最大值是多少。您的伪代码有一些我在程序中编辑的问题。但是你的答案并没有涵盖像1234578910这样的东西。(它没有6,但有所有其他数字)。非常感谢。@titansarus你的问题特别提到它是一组[1,n]范围内的整数。根据这个公式,你的1234578910难题是没有意义的。@erip我说n=10。例如{1,2,3,4,5,7,8,9,10}是{1,2,…,10}的子集,但是上面的算法不生成它。我的问题是不带循环、数组和字符串的发电机组。。。只使用纯递归。我在互联网上检查了很多算法,但即使是最简单的算法也至少使用了循环或字符串。“我没有发现任何东西能同时满足我所有的问题条件。”泰坦萨勒斯经过思考后,我补充了另一个答案。这是相当优雅的,虽然不是尾部调用优化(你可能会有所贡献)。它用
powerset k
定义了
powerset(k+1)
。非常感谢。但是你能编辑的是它会先打印最大的。我的意思是像我的例子一样,从最大的子集(集合本身)到空集合?我是编程新手,这是我第一次需要使用这些位运算符。还有另一个问题:没有位运算符,有没有办法解决这个问题?(也不使用循环和数组)。我是说像河内塔的问题?我还必须补充一点,那就是更新了词典排序的源代码。如果没有数组和位,我无法递归地求解它,但是
n再次感谢你!如果两天内没有答案,我会接受你的答案作为最佳答案。再次感谢您。如果递归算法被编写为纯函数,仅在执行路径末尾使用技术调用“尾部调用优化”进行递归,则可以由充分优化的编译器或解释器进行翻译。
let powerset k =
   match k with
   | [] -> [[]]
   | x:xs -> x * powerset xs + powerset xs
let powerset k =
   let (*) e ess = map (es -> e::es) ess
   reduce (e ess -> (e * ess) ++ ess) [ [] ] (reverse k)
let rec powerset k =
  match k with
  | []    -> [ [] ]
  | x::xs -> 
      let withoutE = powerset xs
      let withE = List.map (fun es -> x::es) withoutE
      List.append withE withoutE
let rec powerset = function
  | []    -> [ [] ]
  | x::xs -> List.append (List.map (fun es -> x::es) (powerset xs)) (powerset xs)
let rec powerset2 k = 
  let inline (++) a b = List.concat [a;b]
  let inline (+*) a bs = List.map (fun b -> a::b) bs
  List.fold 
    (fun ac a -> (a +* ac) ++ ac)
    [ [] ] 
    (List.rev k)