如何找出6个小于33大于0的整数,加起来正好是150,可以重复,比如[25,25,25,25,25,25],但是不需要考虑顺序

   for(int i1=1;i1 <33;++i1)
    for(int i2=1;i2 <33;++i2)
        for(int i3=1;i3 <33;++i3)
            for(int i4=1;i4 <33;++i4)
                for(int i5=1;i5 <33;++i5)
                    for (int i6 = 1; i6 < 33; ++i6)
                    {
                        int sum =i1+i2+i3+i4+i5+i6;
                        if(sum!=150)continue ;
                    }


 

/*如何找出6个小于33大于0的整数,加起来正好是150,可以重复,比如[25,25,25,25,25,25],但是不需要考虑顺序
 *也就是说[26,24,25,25,25,25]和[24,26,25,25,25,25]是同一个组合
 *
 *这个问题来源于CSDN,我也思考了很久,刚开始想到了弹簧的特性,确实能找到一系列的集合,但是不全
 * 问题来源:http://topic.csdn.net/u/20091116/20/b8e6f325-d14f-4133-abbb-220180576f9f.html
 *
 * 有很多人回答了各自的思路,基本上思路都是穷举6个数字,看加起来等于不等于150,但是存在一个严重的问题,就是直接组合的话规模太大,33的6次方个组合=12,914,679,69个组合,
 * 每次组合都需要判断 数字1+数字2+数字3+数字4+数字5+数字6是不是等于150
 * 类似于下面的方法


 * 这种方式在本机器经过测试,没有任何输出的情况下大约需要10秒的时间
 * 在需要用户交互的程序里面,10秒钟的等待基本上会让人抓狂,痛定思痛,必须找个改进的方法
 * 想到了弹簧,6个弹簧串起来,长度固定在150,这6根弹簧有如下特定
 * 1,长度最长能拉到33
 * 2,最短能压缩到1
 * 3,弹簧的压缩系数相同
 * 4,弹簧的自身的重量忽略不计
 * 在弹簧的两端加上两个重物,那么我考虑这串弹簧的两个状态,水平状态和垂直状态
 * 1,在水平状态下,每根弹簧的长度肯定都是25,不考虑摩擦,即[25,25,25,25,25,25]
 * 2,在垂直状态下,弹簧受重物拉伸,弹簧长度就会变为[33,33,33,33,17,1],也就是往一边压缩到不能压缩为止
 * 受此启发,想到一个不浪费CPU的新算法,该算法只计算所有的从大到小的组合排列,因为前提说明了不关心顺序
 * 1,将长度为6的整型数组长度初始化为平均值(150除6=25),如果不能整除,则保证大的数字在前面,比如要求总长度是153的话,那么就是[26,26,26,25,25,25]
 * 2,将第一个整数加1,然后对后面的5个数字继续用150减2的值6求平均,再对第二个数字加1,然后对后面的4个数字用150-26-26的值求平均,递归这个过程,一直到前5个数字都不能再加1为止,也就是成了[33,33,33,33,17,1]
 * 3,递归的约束条件:a,所有的数字大于0但是小于34. b,对任意大于0小于6的整数n,满足Arr[n]<=Arr[n-1],也就是后一个不能超过前一个
 * 3,递归的结束条件是:剩余的可以分配到后续弹簧上的长度小于后面弹簧的个数,或者后面剩下的弹簧个数为1
 *
 * 此算法的正确性可以数学证明
 * 算法性能,当前问题
 * 按照当前问题的条件           
 *          int sum = 150;
            int count = 6;
            int max = 33;
            int min = 1;
 * 在调试版本下大约需要:296.875毫秒秒时间
 * 在Release版本下该精度的时间统计不了,也就是效益0.0001毫秒
 * 差别在于Release版本下没有输出
 */

  

using System;
using System.Collections.Generic;
using System.Text;

namespace FindNum
{
    class Program
    {
        //算法第一步的求平均值得算法1
        static int[] MakeMostAvgArray(int sum, int len)
        {
            int[] ret=new int[len];
            MakeMostAvgArray(ret, sum, 0); 
            return ret;
        }
        //算法第一步的求平均值得算法2
        static void MakeMostAvgArray(int[] data,int sum, int cursor)
        {
            int len = data.Length - cursor;
            int avg = sum / len;
            int mod = sum % len;
            for (int i = cursor; i < data .Length; ++i)
                data[i] = avg;
            for (int i = cursor; i < mod+cursor; ++i)
                ++data[i];
        }
        /// <summary>
        /// 算法第二步:递归
        /// </summary>
        /// <param name="data">数组</param>
        /// <param name="sum">要求的和,这个题目而言要求的是和等于150</param>
        /// <param name="cursor">当前递归到第几个数字</param>
        /// <param name="max">所有数的最大值,这里为33</param>
        /// <param name="min">所有数的最小指,这里为1</param>
        static void DoMakeNum(int[] data,int sum,int cursor,int max,int min)
        {
            System .Diagnostics .Debug .Assert(max >=0);
            if (sum / (data.Length - cursor) < min) return;//递归的结束条件1:剩余的可以分配到后续弹簧上的长度小于后面弹簧的个数
            if (cursor + 1 == data.Length)//递归的结束条件2:或者后面剩下的弹簧个数为1
#if DEBUG
            {
                for (int i = 0; i < data.Length; i++)
                {
                    Console.Write("{0} ", data[i]);
                }
                Console.WriteLine();
                return;
            }
#endif

            for (;TestCursor(data,cursor,max); ++data[cursor])
            {
                int newSum=sum - data[cursor];
                MakeMostAvgArray(data, newSum, cursor + 1);
                DoMakeNum(data, newSum, cursor + 1, max,min);
            }
        }
        //递归的约束条件
        static bool TestCursor(int[] data, int cursor, int max)
        {
            
            if (data[cursor] > max) return false;//a,所有的数字大于0但是小于34
            if (cursor == 0) return true;//第一个数字不受第二个约束条件限制
            if (data[cursor] > data[cursor - 1]) return false;//b,对任意大于0小于6的整数n,满足Arr[n]<=Arr[n-1],也就是后一个不能超过前一个
            return true;
        }
        private static void MakeNum(int sum, int count, int max, int min)
        {
            int[] data = MakeMostAvgArray(sum, count);
            DoMakeNum(data, sum, 0, max, min);
        }
        static void Main(string[] args)
        {
            DateTime start = DateTime.Now;
            int sum = 150;
            int count = 6;
            int max = 33;
            int min = 1;
            MakeNum(sum, count, max, min);

            DateTime end = DateTime.Now;
            TimeSpan ts = end - start;
            Console.WriteLine(ts.TotalSeconds);
            Console.WriteLine(ts.TotalMilliseconds);
            Console.Read();
        }
    }
}


 

 

测试结果:有7352个组合,第一个组合为[25 25 25 25 25 25 ],最后一个组合为[33 33 33 33 17 1]

调整以下调教,如果所有的数字都要大于23,则所有组合如下

25 25 25 25 25 25
26 25 25 25 25 24
26 26 25 25 24 24
26 26 26 24 24 24
27 25 25 25 24 24
27 26 25 24 24 24
27 27 24 24 24 24
28 25 25 24 24 24
28 26 24 24 24 24
29 25 24 24 24 24
30 24 24 24 24 24

其他的没有测试

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值