Hailstone序列问题
3n+1问题是一个简单有趣而又没有解决的数学问题。这个问题是由Collatz在1937年提出的。克拉兹问题(Collatz problem)也被叫做hailstone问题、3n+1问题、Hasse算法问题、Kakutani算法问题、Thwaites猜想或者Ulam问题。
问题如下:
(1)输入一个正整数n;
(2)如果n=1则结束;
(3)如果n是奇数,则n变为3n+1,否则n变为n/2;
(4)转入第(2)步。
克拉兹问题的特殊之处在于:尽管很容易将这个问题讲清楚,但直到今天仍不能保证这个问题的算法对所有可能的输入都有效——即至今没有人证明对所有的正整数该过程都终止。
例如:n=9时,有 9 28 14 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1。
编写程序计算冰雹序列:
import java.util.Arrays;
public class HailstoneSequence {
public static void main(String[] args) {
String[] temp = getHailstoneLength(7).split(",");
System.out.println("序列长度:" + temp.length);
System.out.println(Arrays.toString(temp));
}
public static String getHailstoneLength(int number){
StringBuilder sb = new StringBuilder(number + "");
while (number > 1) {
number = (number % 2 == 0) ? number / 2 : 3 * number + 1;
sb.append("," + number);
}
return sb.toString();
}
}
以上的程序的正确性看似是不言而喻的,但是仔细观察:n的大小和序列的长度似乎是不成正比的,有些很小的数例如:27的序列就比较长。那么问题来了:对于任意的一个序列,长度是否是有限的?目前还没有结论。——以上的小程序未必是一个算法。
生产者和消费者问题
这是一个经典的同步、等待与唤醒的问题。
生产者不断生产信息,并将信息放在一个指定的区域之中;而消费者则从此区域中不断取走数据。
分析:既然生产的是信息,就可以定义一个信息类,生产者和消费者都占有这个信息类的引用——可以通过信息类将生产者和消费者两个线程联系在一起。
在本例中,生产者生产两类信息:Tom—>tom@tom.com 和Jack—>jack@jack.com 。这两种信息交替出现。
信息类Info
public class Info {
private String name ;
private String email ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
生产者类Producer
public class Producer implements Runnable{
private Info info; // 持有信息类的引用
/**
* 通过构造方法设置info的内容
* @param info
*/
public Producer(Info info) {
this.info = info;
}
@Override
public void run() {
boolean flag = false; // 定义标记位
for(int i = 0;i < 50;i++){
if (flag) {
info.setName("Tom");
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setEmail("tom@tom.com");
flag = false;
}else {
info.setName("Jack");
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setEmail("jack@jack.com");
flag = true;
}
}
}
}
消费者类Customer
public class Customer implements Runnable{
private Info info; // 持有信息类的引用
/**
* 通过构造方法给info赋值
* @param info
*/
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(info.getName()+"---->" + info.getEmail());
}
}
}
测试类ProducerAndCustomerTest
public class ProducerAndCustomerTest {
public static void main(String[] args) {
Info info = new Info(); // 生产者和消费者持有的信息类的引用
Producer producer = new Producer(info);
Customer customer = new Customer(info);
new Thread(producer).start();
new Thread(customer).start();
}
}
运行结果:
之所以出现了内容不匹配是因为中间加入了延迟操作,因而可能导致不同步的问题,可以使用同步解决这个问题。
更改代码如下:将姓名和邮箱的设置封装成一个同步方法set方法;将取得姓名和邮箱的操作封装成一个同步方法get方法。
Info.java
public class Info {
private String name ;
private String email ;
/**
* 设置信息的同步方法
* @param name
* @param email
*/
public synchronized void set(String name,String email){
this.name = name;
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.email = email;
}
/**
* 取出信息的同步方法
*/
public synchronized void get(){
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.name + "--->" + this.email);
}
}
Producer.java
public class Producer implements Runnable{
private Info info; // 持有信息类的引用
/**
* 通过构造方法设置info的内容
* @param info
*/
public Producer(Info info) {
this.info = info;
}
@Override
public void run() {
boolean flag = false; // 定义标记位
for(int i = 0;i < 50;i++){
if (flag) {
info.set("Tom", "tom@tom.com");
flag = false;
}else {
info.set("Jack", "jack@jack.com");
flag = true;
}
}
}
}
Customer.java
public class Customer implements Runnable{
private Info info; // 持有信息类的引用
/**
* 通过构造方法给info赋值
* @param info
*/
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
info.get();
}
}
}
以上代码并没有达到设置一个取走一个的效果。
必须依靠一种等待和唤醒的机制。可以增加一个boolean
类型的标志位,此标志位为true
表示可以生产但不能取走,此时的消费者线程应该等待;如果标志位为false
表示可以取走但是不能生产,此时的生产者应该等待。
直接修改Info类,增加等待和唤醒机制
Info.java
public class Info {
private String name ;
private String email ;
boolean flag = true; // 设置标志位,刚开始的时候可以生产但不能取走
/**
* 生产信息的同步方法
* @param name
* @param email
*/
public synchronized void set(String name,String email){
if(!flag){
try {
super.wait(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.email = email;
flag = false; // 表示可以取走
super.notify();
}
/**
* 取出信息的同步方法
*/
public synchronized void get(){
if (flag) {
try {
super.wait(); // 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(90);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.name + "--->" + this.email);
flag = true; // 表示可以生产
super.notify();
}
}
运行结果: