Java实现排列组合

百度百科:
排列,就是指从给定个数的元素中取出指定个数的元素进行排序。
组合,则是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。
排列组合的中心问题是研究给定要求的排列和组合可能出现的情况总数

问题:根据摇色子猜点数。求出所有色子组合的概率。

Input

一个正整数n,表示有多少个骰子,n不超过11。

Output

每行一个double类型的数据,表示组合为1、2、3…点数的概率,结果保留十位小数。

  • 用参考文章的第一个问题的方法,用到的时间为950ms,超时差不多5倍。
    认真观察结果间的关系,明显具有对称关系,用参考文章的方法并不适用
    在这里插入图片描述

在这里插入图片描述虽然参考文章的问题和色子这题确实本质上一致,但色子一题有明显的特性,所以应该换个思路想,不是列出全排列的所有取值可能,然后算它的概率,而是列出符合我要求的值的所有取值可能,然后算它的概率

不是这样
在这里插入图片描述
而是,当色子数为3时,我想要求出总值为3~3+Math.ceil((arr.length-1)/2)的概率,即总值为3 ~ 10的概率。arr是存放所有取值的数组。剩下的11 ~18的概率可以复制3 ~10的。
在这里插入图片描述
找出的排列应该是上面这样的

这时候问题变成了整数划分问题。但是这个整数划分对划分的参数个数有限制,比如,要求总值为4的全排列,但是要求参数只能为3个,因为色子只有3个。感觉整数划分也挺难的,因为整数划分一般都是计算有几种划分方式。被自己给整无语了。

在这里插入图片描述
再看看参考文章的排列结果,再一个循环内总值一定是慢慢往上升的,只要限制住总值,掐灭苗头,好像就可以了。依旧超时5倍。
在这里插入图片描述
算了,把代码放下,再见。

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack;
import java.util.stream.Collectors;

public class Dice {
	public static Stack<Integer> stack = new Stack<Integer>();
//public static List<Integer> list=new ArrayList<Integer>();
	//map存放值和对应概率
	public static Map<Integer,Double>map=new HashMap<Integer,Double>();
	public static int n=0;
	public static double temp=0;
	public static int sumTemp=0;
	public static  int maxSum=0;
	
	public static void main(String[]args ){
		
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		double p=Math.pow(1/6d,n);
//		System.out.println("p="+p);
		int shu[] = {1,2,3,4,5,6};
		//这里先把值全输入
		for(int i=n;i<=6*n;i++){
			map.put( i, 0.0 );
		}
		sumTemp=n+(int)Math.ceil((map.size()-1)/2);
//		System.out.println("sumTemp"+sumTemp);
	
		f(shu,n,0);
		maxSum=6*n+n;
//		System.out.println("maxsum"+maxSum);
		for(int i=n;i<=sumTemp;i++){
			 if(map.containsKey(i)){
	     		temp=map.get(i);
	     		map.put(maxSum-i, temp);
//	     		System.out.println("n="+i+"maxSum-n="+(maxSum-i)+"temp="+temp);
	     	}
		}
//		BigDecimal bd1=new BigDecimal(map.get(3));
//		BigDecimal bd2=new BigDecimal(Math.pow(1/6,3));
//		BigDecimal rs=bd1.multiply(bd2);

//		System.out.println("BigDecimal="+bd2);
		Double d=0.0;
		for(int i=n;i<=6*n;i++){
//			System.out.println("map.get(i)="+map.get(i));
			d=map.get(i)*p;
			System.out.println(String.format("%.10f", d));
//			map.put(n, d);
		}
//		for(Map.Entry<Integer, Double> entry:map.entrySet()){
//		  System.out.println("key="+entry.getKey()+"\tvalue="+entry.getValue());
//		}
	
		
	}
	/**
	 * 这个用来计算每个值出现的组合次数
	 * @param shu 每个参数取值的范围
	 * @param targ 总共取几个参数
	 * @param cur 当前正在的第几个参数的下标
	 */
	private static void f(int[] shu, int targ, int cur) {
        // TODO Auto-generated method stub
        if(cur == targ) {
//            System.out.println(stack);
            int sum=0;
            for(int i=0;i<n;i++){
            	sum+=stack.elementAt(i);
            	if(sum>sumTemp){
            		return;
            	}
            }
            if(map.containsKey(sum)){
//            	System.out.println("sum="+sum);
        		temp=map.get(sum);
        		map.put(sum, temp+1);
        	} 
            return;
        }
         
        for(int i=0;i<shu.length;i++) {
            stack.add(shu[i]);
            f(shu, targ, cur+1);
            stack.pop();
             
        }

    }


}

参考文章:zzl的日常work

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值