Java 非递归实现的排列组合中的平均分组问题

问题:对于N个数字,输出所有分M组,组间和组内的顺序无关的序列。

解析:排列组合问题的平均分组问题。网上没有找到现成的,就写了一个。 Java 编程,调试通过

设G(N,M) 为分组函数,程序的思想是

- M组内每组最小值按组间从小到大输出

- 对于第一组,1肯定是第一个元素,没有其他情况,则其他第一组内其他元素C(N-1,N/M-1),C为组合数,也就是从N-1中选N/M-1个元素填到第一组,对于其每种情况,第二组同样处理,这样,推导到出G(N,M)=C(N-1,N/M-1) * G(N-N/M,M-1)。

算出多少中情况不难,难在输出这些序列。

 

缘起:女儿小学一年级的思维训练题


题目可以简化为 1选7,然后,6分3组 

G(6,3) = C(5,1)*G(4,2) = C(5,1)*C(3,1) = 15种

这里的代码输出了15种情况,为

[1]1 2 | 3 4 | 5 6 | 

[2]1 2 | 3 5 | 4 6 | 

[3]1 2 | 3 6 | 4 5 | 

[4]1 3 | 2 4 | 5 6 | 

[5]1 3 | 2 5 | 4 6 | 

[6]1 3 | 2 6 | 4 5 | 

[7]1 4 | 2 3 | 5 6 | 

[8]1 4 | 2 5 | 3 6 | 

[9]1 4 | 2 6 | 3 5 | 

[10]1 5 | 2 3 | 4 6 | 

[11]1 5 | 2 4 | 3 6 | 

[12]1 5 | 2 6 | 3 4 | 

[13]1 6 | 2 3 | 4 5 | 

[14]1 6 | 2 4 | 3 5 | 

[15]1 6 | 2 5 | 3 4 | 

 

代码

其主要函数 SplitGroup2::next_sg()用来输出下一组组合。

 

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SplitGroup2 {
	private List<Integer> D = new ArrayList<Integer>();
	private int N = 0;
	private int M = 0;
	private int id = 0;

	public List<Integer> next_sg() {
		boolean isfound = false;
		if (D.size() == 0) {
			for (int i = 1; i <= N; i++)
				D.add(i);
			isfound = true;

		} else {

			int r = M - 2;
			int rsize = N / M;

			while (isfound == false) {
				int c0 = r * rsize;
				int cX = c0 + rsize - 1;
				if ((r < 0) || (c0 == cX) || cX < 1) // one element one group
				{
					break;
				}

				while ((cX != c0) && (isfound == false))
				{

					List<Integer> s1 = D.subList((r+1)*rsize, D.size());
					Collections.sort(s1);
					int cc = cX;
					int val = D.get(cc);
					for (int i = 0; i < s1.size(); i++) {
						if (s1.get(i) > val) {
							isfound = true;
							int tmp = D.set(cc, s1.get(i));
							D.set(i+(r+1)*rsize, tmp);
							cc++;
							if(cc > c0 + rsize -1)
								break;
						}
					}

					if(isfound)
					{
						List<Integer> slast = D.subList((r+1)*rsize, D.size());
						Collections.sort(slast);
						
					}
					if (isfound == false) {
						cX = cX - 1;
						if (cX == c0) {
							r--;
							if (r < 0)
								break;
						}
					}
				}
			}
		}
		id = (isfound)?id+1:0;
		if(isfound == false)
		{
			D.clear();
		}
		return D;

	}

	public SplitGroup2(int n, int m) {
		N = n;
		M = m;
		if (N < M)
			throw new IllegalArgumentException("N < M !!");
		if (N % m != 0)
			throw new IllegalArgumentException("N mod m <> 0");
		D.clear();
	}

	public void PrintD() {
		if (D.size() > 0) {
			System.out.printf("[%d]\t",id);
			for (int i = 0; i < D.size(); i++)
			{
				System.out.printf("%d ", D.get(i));
				if((i+1) % (N/M) == 0)
					System.out.print("| ");
			}
			System.out.printf("\n");
		} else
			System.out.printf("N/A\n");
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SplitGroup2 sg = new SplitGroup2(6, 3);
		List<Integer> retD;
		do {
		//for (int i = 0; i < 30; i++)
			retD = sg.next_sg();
			if(retD.size() > 0)
				sg.PrintD();
		} while (retD.size() > 0);

	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值