组合和排列算法(递归)
Posted
技术标签:
【中文标题】组合和排列算法(递归)【英文标题】:Combination and Permutation Algorithms (recursive) 【发布时间】:2012-10-24 07:32:12 【问题描述】:我正在处理一项 Java 作业,我完全被难住了。
问题是:
使用递归编写一个函数来执行以下操作:您有 X 个不同的卡片。你只有 Y 个信封。 Y 小于或等于 X。对于 X 和 Y 的任何给定值,
当顺序不重要且不允许重复时,显示您可以填充 Y 信封的所有可能方式。 hint: X! / (( X-Y)! * Y!)
当订单很重要并且允许重复时,显示所有可能的方式来填写 Y 信封hint: X^Y
当订单很重要且不允许重复时,显示您可以填写 Y 信封的所有可能方式提示:X! / (X – Y)!
当顺序不重要且允许重复时,显示所有可能的方式来填充 Y 信封提示:(X + Y – 1)! / (Y! * (X – 1)!)
例如,在情况(1)下,if X = J, Q, K, A) and Y = 3
,那么输出应该是:J,Q,K J,Q,A J,K,A Q,K,A.
我不希望任何人发布任何代码,也不想找人帮我解决这个问题!我希望一旦我完成了第一部分(问题a),它将打开闸门。有人可以在制定伪代码算法方面提供一些指导吗,这是我所能得到的:
按顺序填写 Y 信封,卡片增加(ex: X=5, Y=3) 1, 2, 3.
用最高的卡片1, 2, 5
替换最高的信封,递减直到我们找到它的原始值1, 2, 4
。
对每个信封执行此操作,从最高到最低(该号码尚未使用)1, 5, 4 1, 3, 4 5, 3, 4 2, 3, 4.
这是我在它崩溃之前得到的,因为它缺少 3 个组合 1, 5, 3 3, 4, 5 5, 3, 2.
如果有任何帮助,我将不胜感激,因为这是我将重复的任务,我不想要解决方案,我需要帮助我自己解决问题。 谢谢!
编辑:我已经尝试了概述的所有 3 种解决方案,但我仍然没有得到它。这就是我目前所得到的:
public static void comboNoRep(String[] a, int y, boolean[] used)
if(y == 0)
// found a valid solution.
System.out.println(result);
for(int i=0; i<a.length; i++)
if(!used[i])
used[i] = true;
result = result + a[i];
comboNoRep(a, y - 1, used);
result = result + " ";
used[i] = false;
else
谁能帮忙指出我的缺点?
【问题讨论】:
您可能想在发布之前尝试对此进行研究。这个问题已经解决了很多次了。 @rharrison33:我在发帖之前研究过它,但一无所获,这就是我发帖的原因。 【参考方案1】:你的老师希望你使用递归。
对于给定的 X,如果 Y 为零,答案是什么?使用您的代码解决此问题。
答案是什么,对于给定的 X,如果我免费为您提供 Y = 某个随机整数 n 的解,那么 n + 1 的解是什么?换句话说,如果我告诉你 X = 5, Y = 3 的解是 ... , ... , ... ,你能很容易地找出 X = 5 的解吗? Y = 3 + 1 = 4?
这是一个完全不同的问题的示例:
假设您知道前两个斐波那契数是 1 和 1。那么找到下一个很容易,对吧?它是 2。现在假设你知道前两个是 1 和 2,下一个是 3!如果前两个是2和3,下一个是5!
一些伪代码:
public int fib(int stop)
if (stop < 2) return 1;
return fibHelp(stop - 2, 1, 1);
public int fibHelp(int stop, int oneBelow, int twoBelow)
if (stop == 0) return oneBelow;
return fibHelp(stop - 1, oneBelow + twoBelow, oneBelow);
看看 fibHelp 是如何调用自己的?那是递归!只要确保你有一个停止条件(我的 if 语句)。
对于您的具体问题,不要返回void
,而是让comboNoRep
返回Set<Set<Integer>>
。当y=0
时,返回一个带有一个元素的Set
(一个空的Set
)。 y=1
,返回一个Set
,它通过向较大集合中的每个集合添加一个元素来构建一堆Set
s(在y=1
的情况下Set
为空,等等)。
使用Set
而不是List
,因为您要确保没有重复。
【讨论】:
这不仅仅是递归,这是尾递归。如果 stop Makoto:这是伪代码,他说他不想要真正的代码。我只是想展示这个想法。 伪代码具有使其可转换为实际代码的基础。就像我说的那样,如果 stop 我已将到目前为止我得到的内容添加到我原来的问题中,帮助? @dominicvautour:我不确定result
是什么,我确定它是String
,但您不想以这种方式构建您的列表。我现在正在编辑上面的帖子。【参考方案2】:
你必须探索所有可能的路线:
创建一个空的“解决方案”列表
对于每张卡片 a,您的第一组解决方案首先将卡片放在每个信封中 - x*y 解决方案
对于您选择的每张卡片:重复,从同一组卡片中删除您使用的卡片,直到您完成解决方案并将其放入数组中用完卡片为止
打印数组
【讨论】:
我已将到目前为止我得到的内容添加到我原来的问题中,帮助?【参考方案3】:对于问题一,假设您的卡片被命名为 card_1
到 card_x
。请注意,填充 Y 信封的所有可能方式要么包括card_1
,要么不包括。如果是这样,您已将问题减少到用卡片 2 到 X 填充 Y-1 信封;如果不是,您已将问题减少到用卡片 2 到 X 填充 Y 信封。
希望这是一个足够的提示,可以帮助您,但不会太多。祝你好运!
【讨论】:
我已将到目前为止我得到的内容添加到我原来的问题中,帮助? 您试图退回到for
循环让自己感到困惑。根本不要使用循环,只使用递归调用处理所有数据。 (注意:你需要在你的函数中添加一些额外的参数来跟踪一些簿记类型的东西,比如你可以在随后的递归调用中使用哪些卡片以及哪些卡片已经被选中。)【参考方案4】:
第一部分可以使用埃克塞特算法。更多信息请参考以下链接:
Visit Here: http://www.bearcave.com/random_hacks/permute.html
我不知道如何在 JAVA 中执行此操作,但我的旧 C 代码中的 sn-p 可能会达到目的。
#include <stdio.h>
//Print Function
void print(const int *arr, const int size)
int i;
if (arr != 0)
for (i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
//Permute Function
void permute(int *arr, const int start, const int sets, const int len)
int i,tmp;
if (start == sets-1)
print(arr, sets);
else
for (i = start; i < len; i++)
tmp = arr[i];
arr[i] = arr[start];
arr[start] = tmp;
permute(arr, start+1, sets, len); //<-- Recursion
arr[start] = arr[i];
arr[i] = tmp;
int main()
int sets,arr[] = 1, 2, 3, 4, 5;
//Accept Number Of Sets To Form
printf("Enter Number Of Sets: ");
scanf("%d",&sets);
//Call Permute Function
permute(arr, 0, sets, sizeof(arr)/sizeof(int));
return 0;
【讨论】:
以上是关于组合和排列算法(递归)的主要内容,如果未能解决你的问题,请参考以下文章