求给定值范围内的所有完数

4 篇文章 0 订阅
package org.study;

/**
 * 功能: 输入给定值,求该值范围内的完数。
 * 完数: 一个数的所有因子之和等于数本身。
 * 例如: 6=1+2+3;
 */
public class PerfectNumber 
{
	//保存所有因子值;
	private int[] factors;
	//有效因子数目,同时也做index用。
	private int factorSize;

	public PerfectNumber()
	{
		this(512);
	}

	public PerfectNumber(int factorNum)
	{
		factors = new int[factorNum];
		factorSize=0;
		//任何数的第一个因子为1
		factors[factorSize++]=1;
	}

	public void reset()
	{
		//factors[0]=1 有效
		factorSize=1;
	}

	/**
	 * 求输入值范围内的所有完数,并打印。
	 */
	public void getAllPerfectNumbers(int limit)
	{
		for(int i=4; i<= limit; i++)
		{
			//重置factors的有效标志位
			reset();
			if(isPerfectNumber(i))
			{
				System.out.print(i+"\t");
			}
		}
	}

	/**
	 * 判断输入的数,是否是完数。若是返回true,否则返回false。
	 */
	public boolean isPerfectNumber(int input)
	{
		int mid =(int)Math.sqrt(input);
		int another = 0;
		int sum = 0;

		//一个数的因子有小的部分和大的部分,求到小的就可以得到大的。
		for(int i=2; i<=mid; i++)
		{
			if(input % i == 0)
			{
				factors[factorSize++] = i;
				another = input / i;
				//若它的另一个因子不同则保存,否则丢弃。
				if(another != i)
				{
					factors[factorSize++] = another;
				}
			}
		}
		//求所有因子之和
		for(int i=0; i<factorSize; i++)
		{
			sum+=factors[i];
			//System.out.println("input = "+input+", factors["+i+"] = "+factors[i]);
		}
		if(sum == input)
			return true;
		else
			return false;
	}

	public static void main(String[] args) 
	{
		PerfectNumber pn = new PerfectNumber();
		//打印出1000范围内的所有完数
		pn.getAllPerfectNumbers(1000);
	}
}

该方法求解1000, 0000以内的完数,耗时130多秒,求解到4个完数。
后来经过思索,想到了多线程方式求解,改进了一下时间。程序如下:
package org.study;

import java.util.concurrent.CountDownLatch;

/**
 * 功能: 输入给定值,求该值范围内的完数。
 * 完数: 一个数的所有因子之和等于数本身。
 * 例如: 6=1+2+3;
 */
public class PerfectNumber implements Runnable
{
	private static final int DEFAULT = 512;
	//保存所有因子值;
	private int[] factors;
	//有效因子数目,同时也做index用。
	private int factorSize;
	private int start;
	private int end;

	private CountDownLatch cdl;

	public PerfectNumber()
	{
		this(DEFAULT);
	}

	public PerfectNumber(int factorNum)
	{
		factors = new int[factorNum];
		factorSize=0;
		//任何数的第一个因子为1
		factors[factorSize++]=1;
	}

	public PerfectNumber(int start, int end, CountDownLatch cdl)
	{
		this(DEFAULT);
		this.start = start;
		this.end = end;
		this.cdl = cdl;
	}

	public void reset()
	{
		//factors[0]=1 有效
		factorSize=1;
	}	

	/**
	 * 判断输入的数,是否是完数。若是返回true,否则返回false。
	 */
	public boolean isPerfectNumber(int input)
	{
		int mid =(int)Math.sqrt(input);
		int another = 0;
		int sum = 0;

		//一个数的因子有小的部分和大的部分,求到小的就可以得到大的。
		for(int i=2; i<=mid; i++)
		{
			if(input % i == 0)
			{
				factors[factorSize++] = i;
				another = input / i;
				//若它的另一个因子不同则保存,否则丢弃。
				if(another != i)
				{
					factors[factorSize++] = another;
				}
			}
		}
		//求所有因子之和
		for(int i=0; i<factorSize; i++)
		{
			sum+=factors[i];
			//System.out.println("input = "+input+", factors["+i+"] = "+factors[i]);
		}
		if(sum == input)
			return true;
		else
			return false;
	}
	
	public void run()
	{
		for(int i=start; i<= end; i++)
		{
			//重置factors的有效标志位
			reset();
			if(isPerfectNumber(i))
			{
				System.out.print(i+"\t");
			}
		}
		//线程执行完成,线程计数器减1
		cdl.countDown();
	}

	public static void main(String[] args) 
	{
		final int limit = 10000000;		
		final int THREAD_NUM = 5;

		CountDownLatch cdl = new CountDownLatch(THREAD_NUM);

		//打印出1000范围内的所有完数
		System.out.println("All perfect numbers of "+limit);
		java.util.Date start = new java.util.Date();
		
		for(int i=0; i< THREAD_NUM; i++)
		{
			Runnable pn = new PerfectNumber(limit*i/THREAD_NUM+1, limit*(i+1)/THREAD_NUM, cdl);
			Thread t = new Thread(pn);
			t.start();
		}		
		
		//等待所有线程都执行完成
		try
		{
			cdl.await();	
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

		java.util.Date end = new java.util.Date();
		System.out.println("\n cost "+(end.getTime()-start.getTime())/1000.0+" s");
	}
}


经过改进,使用多线程方式求解,使用5个线程求解耗时69.406秒, 使用10个线程求解耗时65.453秒。多线程也只能到这里。下一步看能不能优化算法了?


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值