简介
共同随机数(Common random number)以下,简称CRN,实现相同的样本在不同的运算单元中从集合中随机选出一个共同的样本作为共同随机数。
社群随机选举算法基于共同随机数算法,以下简称ACRN,实现每个参与者从参与者提供的共同随机数集合中选出共同的随机数,提供该随机数的参与者为随机选举出的代表,且只要样本相同,遵循共同的算法,选举出代表为共同认可的并且是随机选出的代表。
以下从使用场景,原理,实现三个方面介绍了2个算法。
应用场景
彩票摇奖,共同参与产生充满信任的真随机数;
区块链通过随机选举出共同节点防止作弊(时间证明共识算法);
共同随机数基本原理
选取一个足够大的共同已知随机分布的数字集合,本算法使用了圆周率π。
每一个参与者贡献自己的随机数,得到一个共同已知的随机数集合,以a代替。求集合a的和sum(a),sum(a)作为下标找到π上面的数字r便是共同随机数。
社群随机选举算法的实现原理
由于a集合是由参与者贡献的随机数集合,可以遍历a得到与r有关系的一个数值(可以是与共同随机数差值最小或最大的数值),该数值的贡献者便是在社群中被所有参与者共同认可的随机选举出的参与者。
解决可推测的问题
读到这里或许你已经发现了一个巨大的bug,就是最后一个随机数贡献者的随机数对整个随机数的产出具有可推测性,从而实现”“作弊“,解决这个问题的办法就是”蒙眼睛“,不到随机数产出的最后一刻互不相知社群中任何一个其他成员祭出的随机数,从而阻断对共同随机数的推测行为,但是在计算机世界如何实现蒙眼睛呢,总不能约定一个时间点同时祭出吧,这个同时在计算机世界也是有很大bug的,至少现在没有真正意义上面的实现,因为你无法消除这个时差,只要存在时差仍然可以作弊,而且组合样本的过程需要找一个中间节点,相信中间节点的可靠,也违反了判决公正去中心化的原则。
真正实现计算机”蒙眼睛“解决方案是使用非对称式加密算法,在确定随机数样本集合之前只公布签名和公钥,样本确定之后再公布被签名的随机数。
代码
TIPS:以下代码使用的圆周率,由于圆周率无穷,单个随机样本位数越大程序执行越慢,6位数的计算时间是可接受的,或者后期优化圆周率检索算法可提高查询效率,比如将圆周率足够的位数存入数据库,使用数据库的索引功能能大大提高查询效率。
package com.tccmeta.base.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* <p>Title:使用圆周率实现社群随机选举算法 </p>
* <p>Description:使用圆周率实现社群随机选举算法。 </p>
* <a href="http://www.oursci.org/magazine/200301/030126.htm">使用圆周率实现社群随机选举算法</a>
* @author 1339042328@qq.com
* @version 1.0
*/
public class Pi {
public final BigDecimal result;
BigDecimal upper = BigDecimal.valueOf(426880.0000 * Math.sqrt(10005.0000));
/**
* 初始化圆周率
*/
public Pi(int n, int scale) {
long begin = System.currentTimeMillis();
upper = upper.setScale(scale, RoundingMode.HALF_EVEN);
System.out.println("初始化耗时:" + (System.currentTimeMillis() - begin) / 1000);
begin = System.currentTimeMillis();
BigDecimal downer = BigDecimal.valueOf(0);
for (int i = 0; i <= n; i++) {
BigDecimal d0 = factorial(6 * i).multiply(new BigDecimal(545140134 * i + 13591409));
BigDecimal d1 = factorial(i);
BigDecimal d2 = pow(d1, 3);
BigDecimal d3 = factorial(3 * i);
BigDecimal d4 = pow(new BigDecimal(-640320), 3 * i);
BigDecimal d5 = d2.multiply(d3);
BigDecimal d6 = d4.multiply(d5);
BigDecimal d = d0.divide(d6, RoundingMode.HALF_EVEN);
downer = downer.add(d);
}
result = upper.divide(downer, RoundingMode.HALF_EVEN);
System.out.println("计算耗时:" + (System.currentTimeMillis() - begin) / 1000);
}
private static BigDecimal pow(BigDecimal val, int power) {
BigDecimal value = BigDecimal.valueOf(1);
for (int i = 1; i <= power; i++) {
value = value.multiply(val);
}
return value;
}
public static BigDecimal factorial(int n) {
BigDecimal value = BigDecimal.valueOf(1);
for (int i = 1; i <= n; i++) {
value = value.multiply(BigDecimal.valueOf(i));
}
return value;
}
/**
* 求取共同随机数
*
* @param numbers 样本集合
* @return 共同随机数
*/
public static int choose(int[] numbers) {
//样本的和
int sum = 0;
//选取样本中最大的数的字符长度为截取圆周率数字的长度
int length = 0;
//求样本集合的和
for (int number : numbers) {
sum = sum + number;
int temp = String.valueOf(number).length();
if (temp > length) {
length = temp;
}
}
Pi pi = new Pi(1, sum);
//以样本集合的和减去截取长度为上下标截取圆周率中的字符串为目标随机值
int randomNum = Integer.parseInt(pi.result.toString().substring(sum - length, sum));
int[] arr2 = new int[numbers.length]; //存储与目标随机值的差值
for (int i = 0; i < numbers.length; i++) {
arr2[i] = distance(numbers[i], randomNum);
}
//找出样本集合中最接近目标值的样本
int flag = search(arr2);
System.out.println("最接近目标值"+randomNum+"的数为:" + numbers[flag]);
//返回选中的样本的下标
return flag;
}
/**
* 找出最小值
*/
private static int search(int[] arr) {
int number = 0;
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
number = i;
}
}
return number;
}
/**
* 求出目标值与当前数的距离
*/
private static int distance(int a, int b) {
int dis = 0;
if (a >= b) {
dis = a - b;
} else
dis = b - a;
return dis;
}
public static void main(String[] args) {
int[] numbers = {2124, 4545, 5651, 5535, 6111, 4545, 7878, 9781, 9976};
int index = Pi.choose(numbers);
System.out.println("社群矩阵随机选举出的共同随机数下标为" + index);
System.out.println("提供该位置的随机数的参与者为随机选举出的代表");
}
}