一个数的分解方法(一道面试题)

题意要求:


在满足的以上要求的情况下编程求解出给定一个数求解出所有的可能组合。例如:输入10,则应该返回:

1+9=10

2+8=10

3+7=10

4+6=10

1+2+7=10

1+3+6=10

1+4+5=10

2+3+5=10

1+2+3+4=10

共计9条

 

求解过程:

  拿到题目首先最直观的想法就是暴力求解,显然当给定的和数很大时这种方式将很耗时,这里给出一种求解过程供参考。

依据题意,由于同一个组合中数字无重复,那么我们就可以将数子进行排序,继而有,下面的求解过程就是倒着求解这些数字,即从最后一个最大的数字开始依次求解。首先我们来明确一下n的取值范围,


    

这样我们明确了n的取值范围,下面就是在给定的n的情况下倒叙求解a(n),由于是倒叙求解,所以a(n)以后的数必定已知,这样我们肯定知道从a1到a(n)的和,具体推导过程如下:


综合以上所有推导过程可以得出如下结论:


很明显这是一个递推的过程,并且可以证明这是一个充要条件,即如果组合满足此时一定是题目要求的,并且满足题目要求的组合一定满足这个式子。这里简短的说明一下,对于必要性无可厚非,这是我们由条件推出来的,至于充分性可以由第三个式子看出。下面我们就可以放心的使用这个公式而不必担心会有错漏之处。下面给出了一个java实现:

package com.split.test;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * 将一个指定的数字分解为一些无重复的组合
 * @author Administrator
 *
 */
public class SplitNum {
	public static void main(String[] args) {
		Scanner scanner=new Scanner(System.in);
		System.out.println("请您输入想分解的整数:");
		long sum=scanner.nextLong();
		long start=System.currentTimeMillis();
		long num=split(sum);
		System.out.format("共计%d条,耗时%d ms\n",num,(System.currentTimeMillis()-start));
	}

	/**
	 * @param sum
	 */
	private static long split(long sum) {
		long[] num=new long[]{0l};
		if(sum>2){
			long n_max=(long)Math.floor((Math.sqrt(8*sum+1.0)-1.0)/2.0);
			List<Long> nums=new ArrayList<Long>();
			for(int n=2;n<=n_max;n++){
				nums.clear();
				nums.add(sum);
				split_n(num,n,sum,nums);
			}
		}
		return num[0];
		
	}

	/**
	 * @param num
	 * @param n
	 * @param sum
	 * @param nums
	 */
	private static void split_n(long[] num, int n, long sum, List<Long> nums) {
		if(n>0){
			long a_min=(long)Math.ceil((sum*1.0)/n+(n-1.0)/2.0);
			long a_max=(long)Math.floor(Math.min(sum-n*(n-1.0)/2.0, nums.get(nums.size()-1)-1));
			for(long a=a_min;a<=a_max;a++){
				nums.add(a);
				split_n(num,n-1,sum-a,nums);
				nums.remove(nums.size()-1);
			}
		}else{
			printnums(nums);
			num[0]++;
		}
		
	}

	/**
	 * 打印结果
	 * @param nums
	 */
	private static void printnums(List<Long> nums) {
	
		for(int k=nums.size()-1;k>1;k--){
			System.out.print(nums.get(k)+"+");
		}
		System.out.println(nums.get(1)+"="+nums.get(0));
	}


}


运行结果:

请您输入想分解的整数:

15

7+8=15

6+9=15

5+10=15

4+11=15

3+12=15

2+13=15

1+14=15

4+5+6=15

3+5+7=15

2+6+7=15

3+4+8=15

2+5+8=15

1+6+8=15

2+4+9=15

1+5+9=15

2+3+10=15

1+4+10=15

1+3+11=15

1+2+12=15

2+3+4+6=15

1+3+5+6=15

1+3+4+7=15

1+2+5+7=15

1+2+4+8=15

1+2+3+9=15

1+2+3+4+5=15

共计26条,耗时0 ms



 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值