看得见的算法——概率模拟问题之分钱算法

看得见的算法——概率模拟问题之分钱算法

有这样一个问题在一个环境中有一群人,每个人都有100块钱,每个人都随机的给另外一个人一块钱,问题是一段时间过后财富的情况是怎样的?

这个问题最有意思的地方是在问题的调查中很多人都觉得一段时间后他们的财富值都在100上下波动,事实真的是这样吗我们使用看的见的算法模拟一下:

首先我们要进行视图层的渲染

我们先贴上视图层完整代码然后为大家讲解这个问题的渲染

package com.lipengge.randomquestion.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JFrame;
import javax.swing.JPanel;

import com.lipengge.randomquestion.util.AlgoVisHelper;

public class AlgoFrame extends JFrame{
	/**
	 * 
	 */
	private static final long serialVersionUID = -3035088527551930125L;
	private int canvasWidth;//画布的宽
	private int canvasHeight;//画布的长
	private int[] money;//分钱数组
   public int getCanvasWidth() {
		return canvasWidth;
	}
	public int getCanvasHeight() {
		return canvasHeight;
	}
	public void render(int[] money){
		this.money=money;
		repaint();
	}
   public AlgoFrame(String title,int  canvasWidth, int canvasHeight){
	  super(title);
	  this.canvasWidth=canvasWidth;
	  this.canvasHeight=canvasHeight;
	  setDefaultCloseOperation(EXIT_ON_CLOSE);
	  setResizable(false);//关闭可拖动
	  setVisible(true);
	  AlgoCanvas algoCanvas = new AlgoCanvas();
	  setContentPane(algoCanvas);
	  pack();
   }
   //设置面板JPanel
   class AlgoCanvas extends JPanel{
	/**
	 * 
	 */
	private static final long serialVersionUID = -6056196800598676936L;
    private  AlgoCanvas(){
    	//开启双缓存
    	super(true);
    }
	@Override
	//在面板上绘制内容
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d=(Graphics2D) g;
		AlgoVisHelper.setRenderingHints(g2d);//设置抗锯齿
		AlgoVisHelper.setStrokeWidth(g2d, 1);//设置边的宽度为5
		for(int i=0;i<money.length;i++){
		  if(money[i]>0){
			  AlgoVisHelper.setColor(g2d, Color.BLUE);
			  drawMoney(g2d,i,canvasHeight/2-money[i]);
		  }else{
			  AlgoVisHelper.setColor(g2d, Color.RED);
			  drawMoney(g2d,i,canvasHeight/2);
		  }
		}
	}
	@Override
	public Dimension getPreferredSize() {
		return new Dimension(canvasWidth,canvasHeight);
	}
   }
   public void drawMoney(Graphics2D g2d,int x,int heightStart){
	   int w=canvasWidth/money.length;
	   AlgoVisHelper.fillReactangle(g2d,w*x+1,heightStart,w-1,Math.abs(money[x]));
   }
}

1.首先我们需要一个数组而这个数组是在控制层传递过来的

定义一个私有全局变量money数组下标代表人的编号值代表钱数通过render从控制层渲染过来

在这里插入图片描述

2.在Jpanel里进行绘制逻辑

这里我们让当前人钱数大于零的为蓝色小于的为红色

在这里插入图片描述

drawMoney方法
我们把整个面板的宽度除以人的数量就是每个人矩形块的宽度
这里我们使用填充矩形由于我们窗体坐标坐上角是(0,0)所以我们矩形高的起点应该是面板的高度减去统计矩形的高度我们为了让矩形之间有间隔采用每个矩形的w-1起始位置w+1来解决

在这里插入图片描述

到这里我们的视图层就完了是不是很简单?

控制层逻辑

完整代码
package com.lipengge.randomquestion.controller;

import java.awt.EventQueue;
import java.util.Arrays;

import com.lipengge.randomquestion.util.AlgoVisHelper;
import com.lipengge.randomquestion.view.AlgoFrame;

public class AlgoVisualizer {
	private AlgoFrame frame;
	private int[] money;
	public AlgoVisualizer(int screenWidth,int screenHeight,int Number){
		money=new int[Number];
		//初始化数据
		for(int i=0;i<Number;i++){
			money[i]=100;
		}
		//初始化视图
		EventQueue.invokeLater(()->{
			frame=new AlgoFrame("随机分钱开始问题模拟",screenWidth,screenHeight);
			new Thread(()->run()).start();
		});
	}
	//播放动画
	public void run(){
		while(true){
			//排序这样就无法确定那个人的财富量最大
			Arrays.sort(money);
			//绘制数据
			frame.render(money);
			//线程休眠20ms
			AlgoVisHelper.pause(40);
			//最外层循环意味着50倍的速度
			for(int k=0;k<50;k++){
			for(int i=0;i<money.length;i++){
				//if(money[i]>0){
					int j=(int)(Math.random()*money.length);
					money[i]--;
					money[j]++;
				//}
			}
			}
		}
	}
 public static void main(String[] args) {
  new AlgoVisualizer(1000,1000,100);
  }
}

首先我们要初始化一个money数组
解释一下参数:screenWidth窗体的宽,screenHeight窗体的高,Number人员的数量

在这里插入图片描述

第二点我们写动画逻辑也就是怎样给钱
由于给钱这个过程一直在进行这里给个死循环
这里我们进行一次排序的目的是为了让财富值看起来是有序的但是需要注意的是由于是排序的所以财富值的人员是不固定的比如说:二号在某一时刻财富值最大,排在最前面,下一刻三号变为最大三号就在最前面了而我们能看到的只有财富值并不能看到是哪个具体的人
在for循环里每个人随机给一个人1块钱

在这里插入图片描述

最后就是我们的事件分发器
传入相关参数开启run()(动画逻辑)
如果我们的业务和分发器公用一个线程又可能造成阻塞所以重新开启一个线程然后start

在这里插入图片描述

辅助类

ackage com.lipengge.randomquestion.util;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

public class AlgoVisHelper {
	private AlgoVisHelper(){}
	//画圆描边的辅助类
	public static void strokeCircle(Graphics2D g,int x,int y,int r){
		//圆数据的闭包
		Ellipse2D circle = new Ellipse2D.Double(x-r,y-r,2*r,2*r);
		g.draw(circle);
	}
	//画圆填充的辅助类
	public static void fillCircle(Graphics2D g,int x,int y,int r){
		Ellipse2D circle = new Ellipse2D.Double(x-r,y-r,2*r,2*r);
		g.fill(circle);
	}
	//画矩形的辅助类
	public static void strokeReactangle(Graphics2D g,int x,int y,int width,int height){
		Rectangle2D reactangle = new Rectangle2D.Double(x,y,width,height);
		g.draw(reactangle);
	}
	//填充矩形的辅助类
	public static void fillReactangle(Graphics2D g,int x,int y,int width,int height){
		Rectangle2D reactangle = new Rectangle2D.Double(x,y,width,height);
		g.fill(reactangle);
	}
	//设置画笔颜色
    public static void setColor(Graphics2D g,Color color){
    	g.setColor(color);
    }
    //设置边的宽度和平滑性
    public static void setStrokeWidth(Graphics2D g,int w){
    	//BasicStroke.CAP_ROUND把定点变为圆弧半径为画笔一半
    	//BasicStroke.JOIN_ROUND把线条连接点变为弧形
    	g.setStroke(new BasicStroke(w,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
    }
    //设置抗锯齿
    public static void setRenderingHints(Graphics2D g){
    	RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    	hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    	g.addRenderingHints(hints);
    }
    public static void pause(long millsecond){
    	try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    }
    
}

运行结果分析

在这里插入图片描述

1.我们可以看到一段时间过后其实贫富差距是越来越大的那么这个问题模型是否可以和我们现实中的财富流通挂钩呢答案是否定的因为我们现实中进行货币流通是符合实际根据供求关系是基于市场体制的这个模型显然不符合实际
2.这个问题也说明了我们在这种问题上的思维是有问题的所以当我们遇到此类问题是不妨用程序模拟试试,这是一次大胆的尝试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值