看得见的算法——概率模拟问题之分钱算法
有这样一个问题在一个环境中有一群人,每个人都有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();
}
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);
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);
AlgoVisHelper.pause(40);
for(int k=0;k<50;k++){
for(int i=0;i<money.length;i++){
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){
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.这个问题也说明了我们在这种问题上的思维是有问题的所以当我们遇到此类问题是不妨用程序模拟试试,这是一次大胆的尝试