[返回科技频道首页]·[所有跟帖]·[ 回复本帖 ] ·[热门原创] ·[繁體閱讀]·[版主管理]
点数和速算问题(续)(有改进)
送交者: 孤魂浪子[进士☆] 于 2018-04-21 11:00 已读 1136 次  

孤魂浪子的个人频道

原贴题目:
 有n个骰子,每个骰子m个面,点数从1到m,用这些骰子掷出s点的可能数是多少?
 求能在目前的个人电脑上算出n=100万,m=100万,s=n*(m+1)/2时的算法。
 即若记所求函数为N(n, m, s), 求可计算N(1000000, 1000000, 500000500000)的有效算法。
 我现在的算法能在十几分钟内算到N(10000, 10000, 50005000)。

前贴贴出的算法:
	/**
	 * 指定点数和,计算色子点数的可能排列数.
	 *
	 * @param n
	 *            色子数
	 * @param m
	 *            色子面数
	 * @param s
	 *            点数和
	 * @return 排列数
	 */
	private static BigInteger directN2(int n, int m, int s) {
		s -= n;// 参数的点数是从1开始数的,但公式的点数是从0开始。

		// 根据对称性降低计算量
		if (s > n * (m - 1) / 2) {
			s = n * (m - 1) - s;
		}

		BigInteger N = BigInteger.ZERO;
		BigInteger bignPositive = BigInteger.valueOf(n);
		BigInteger bignNegative = bignPositive.negate();
		TimeMeter tm = new TimeMeter();
		BigInteger divisor = BigInteger.ONE;

		for (int j = 2; j <= n; j++) {
			divisor = divisor.multiply(BigInteger.valueOf(j));
		}
		tm.reportln("计算除数");

		for (int i = 0; i <= s / m; i++) {
			// System.out.printf("%dn", i);

			BigInteger x = i % 2 == 0 ? bignPositive : bignNegative;

			for (int j = s - m * i + 1; j <= s - m * i + n - 1; j++) {
				x = x.multiply(BigInteger.valueOf(j));
			}

			x = x.divide(divisor);

			divisor = divisor.multiply(BigInteger.valueOf(i + 1)).divide(BigInteger.valueOf(n - i));

			N = N.add(x);
		}

		return N;
	}

改进算法:
	/**
	 * 指定点数和,计算色子点数的可能排列数.
	 *
	 * @param n
	 *            色子数
	 * @param m
	 *            色子面数
	 * @param s
	 *            点数和
	 * @return 排列数
	 */
	private static BigInteger directN5(int n, int m, int s) {
		s -= n;// 参数的点数是从1开始数的,但公式的点数是从0开始。

		// 根据对称性降低计算量
		if (s > n * (m - 1) / 2) {
			s = n * (m - 1) - s;
		}

		BigInteger N = BigInteger.ZERO;
		BigInteger bignPositive = BigInteger.valueOf(n);
		BigInteger bignNegative = bignPositive.negate();
		TimeMeter tm = new TimeMeter();
		BigInteger divisor = BigInteger.ONE;

		for (int j = 2; j <= n; j++) {
			divisor = divisor.multiply(BigInteger.valueOf(j));
		}
		tm.reportln("计算除数");

		for (int i = 0; i <= s / m; i++) {
			// System.out.printf("%dn", i);

			BigInteger x = i % 2 == 0 ? bignPositive : bignNegative;

			// 改一个个累乘为两两归并相乘
			BigInteger[] xs = new BigInteger[n - 1];
			for (int j = 0; j < xs.length; j++) {
				xs[j] = BigInteger.valueOf(j + s - m * i + 1);
			}
			for (int k = 0; k < 32 - Integer.numberOfLeadingZeros(xs.length); k++) {
				for (int j = 0; (j + 1) << k < xs.length; j += 2) {
					xs[j << k] = xs[j << k].multiply(xs[(j + 1) << k]);
				}
			}

			x = xs[0].divide(divisor);

			divisor = divisor.multiply(BigInteger.valueOf(i + 1)).divide(BigInteger.valueOf(n - i));

			N = N.add(x);
		}

		return N;
	}

在我的计算机上计算N(10000, 10000, 50005000)的结果:
  改进前:1832秒
  改进后: 652秒
效果还是比较明显的。不过不是数量级的提升,不太满意。

评分完成:已经给 孤魂浪子 加上 200 银元!

喜欢孤魂浪子朋友的这个贴子的话, 请点这里投票,“赞”助支持!
[举报反馈]·[ 孤魂浪子的个人频道 ]·[-->>参与评论回复]·[用户前期主贴]·[手机扫描浏览分享]·[返回科技频道首页]
帖子内容是网友自行贴上分享,如果您认为其中内容违规或者侵犯了您的权益,请与我们联系,我们核实后会第一时间删除。

所有跟帖:        ( 主贴楼主有权删除不文明回复,拉黑不受欢迎的用户 )


用户名:密码:[--注册ID--]

标 题:

粗体 斜体 下划线 居中 插入图片插入图片 插入Flash插入Flash动画


     图片上传  Youtube代码器  预览辅助

手机扫描进入,浏览分享更畅快!

楼主本栏目热帖推荐:

>>>>查看更多楼主社区动态...






[ 留园条例 ] [ 广告服务 ] [ 联系我们 ] [ 个人帐户 ] [ 版主申请 ] [ Contact us ]