斐波那契散列算法和hashMap实践
斐波那契散列和hashMap实践
适合的场景:抽奖(游戏、轮盘、活动促销等等)
如果有不对的地方,欢迎指正!
HashMap实现数据散列:
配置项目,引入pom.xml:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
前置条件:
- 存放数组:128位
- 100个数据进行散列
- 若有hash冲突,使用拉链法
首先,初始化100个随机数,这里采用雪花算法snowFlake,采用灵活注解引用,声明为Component,
简单了解下SnowFlake工具类实现方式:
import com.example.containstest.containsTestDemo.mapper.FileNameAndType;
import com.example.containstest.containsTestDemo.mapper.FileNameInsertMapper;
import com.example.containstest.containsTestDemo.pojo.DatagenertionDao;
import com.example.containstest.containsTestDemo.pojo.FileNameType;
import com.example.containstest.containsTestDemo.utils.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class SnowFlake implements IIdGenerator {
private Snowflake snowflake;
@PostConstruct
public void init(){
// 0 ~ 31 位,可以采用配置的方式使用
long workerId;
try {
workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
}catch (Exception e){
workerId = NetUtil.getLocalhostStr().hashCode();
}
workerId = workerId >> 16 & 31;
long dataCenterId = 1L;
snowflake = IdUtil.createSnowflake(workerId,dataCenterId);
}
@Override
public long nextId() {
return snowflake.nextId();
}
}
循环100,取其随机数保存列表中:
List<String> list = new ArrayList<>();
//保存idx和重复的值
Map<Integer, String> map = new HashMap<>();
for(int i = 0; i < 101; i++){
list.add(String.valueOf(snowFlake.nextId()));
}
创建数据散列到的数组大小,这里取128
//定义要存放的数组 模拟初始化为128
String[] res = new String[128];
遍历保存的数组,计算出当前数值的hash值,然后到数组对应的下标处对应;
- 为空。当前
key赋值到该数组下标值 - 不为空,表示
hash冲突,这里采用字符串拼接模拟碰撞后使用的拉链法 map存储对应idx和key值- 对重复的散列的值进行排序输出
for(String key : list){
//计算hash值,未使用扰动函数
int idx = key.hashCode() & (res.length - 1);
log.info("key的值{},idx的值{}",key,idx);
if(null == res[idx]){
res[idx] = key;
continue;
}
res[idx] = res[idx] +"->" + key;
map.put(idx,res[idx]);
}
//排序
mapSort(map);
map排序:
private void mapSort(Map<Integer, String> map) {
// 按照Map的键进行排序
Map<Integer, String> sortedMap = map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
)
);
log.info("====>HashMap散列算法碰撞数据:{}",JSON.toJSONString(sortedMap));
}
未使用扰动函数HashMap散列输出结果展示:
{
28: "1596415617815183397->1596415617815183430",
29: "1596415617815183398->1596415617815183431",
30: "1596415617815183399->1596415617815183432",
59: "1596415617815183363->1596415617815183440",
60: "1596415617815183364->1596415617815183441",
61: "1596415617815183365->1596415617815183442",
62: "1596415617815183366->1596415617815183443",
63: "1596415617815183367->1596415617815183400->1596415617815183444",
64: "1596415617815183368->1596415617815183401->1596415617815183445",
65: "1596415617815183369->1596415617815183402->1596415617815183446",
66: "1596415617815183403->1596415617815183447",
67: "1596415617815183404->1596415617815183448",
68: "1596415617815183405->1596415617815183449",
90: "1596415617815183373->1596415617815183450",
91: "1596415617815183374->1596415617815183451",
92: "1596415617815183375->1596415617815183452",
93: "1596415617815183376->1596415617815183453",
94: "1596415617815183377->1596415617815183410->1596415617815183454",
95: "1596415617815183378->1596415617815183411->1596415617815183455",
96: "1596415617815183379->1596415617815183412->1596415617815183456",
97: "1596415617815183413->1596415617815183457",
98: "1596415617815183414->1596415617815183458",
99: "1596415617815183415->1596415617815183459",
121: "1596415617815183383->1596415617815183460",
125: "1596415617815183387->1596415617815183420",
126: "1596415617815183388->1596415617815183421",
127: "1596415617815183389->1596415617815183422"
}
针对上述代码,修改int idx = key.hashCode() & (res.length - 1);为下面:
int idx = (res.length - 1) & (key.hashCode() ^ (key.hashCode() >>> 16));
使用扰动函数HashMap散列输出结果展示:
{
44: "1596518378456121344->1596518378456121388",
67: "1596518378460315650->1596518378460315694",
72: "1596518378456121351->1596518378456121395",
73: "1596518378456121350->1596518378456121394",
83: "1596518378456121345->1596518378456121389",
92: "1596518378460315651->1596518378460315695",
93: "1596518378460315652->1596518378460315696"
}
从对比结果可以看到,加入扰动函数后hash碰撞减少了很多。
斐波那契散列算法
前置条件:
- 生成模拟数据:随机且不重复的100个数
- 声明散列数组:大小128
- 若有hash冲突,保存map,方便数据查看
静态变量声明:
//黄金分割点
private static final int HASH_INCREMENT = 0x61c88647;
private static int range = 100;
按照惯例,初始化数组,模拟数据;
List<Integer> listThreadLocal = new ArrayList<>();
Map<Integer, String> map = new HashMap<>();
//定义要存放的数组 模拟初始化为128
Integer[] result = new Integer[128];
result = getNumber(range);
//......
//-----方法
/**
* 随机生成100以内不重复的数
* @param total
* @return
*/
public static Integer[] getNumber(int total){
Integer[] NumberBox = new Integer[total]; //容器A
Integer[] rtnNumber = new Integer[total]; //容器B
for (int i = 0; i < total; i++){
NumberBox[i] = i; //先把N个数放入容器A中
}
Integer end = total - 1;
for (int j = 0; j < total; j++){
int num = new Random().nextInt(end + 1); //取随机数
rtnNumber[j] = NumberBox[num]; //把随机数放入容器B
NumberBox[num] = NumberBox[end]; //把容器A中最后一个数覆盖所取的随机数
end--; //缩小随机数所取范围
}
return rtnNumber; //返回int型数组
}
遍历模拟的数据,通过源码阅读,可以找到new ThreadLocal<String>().set("xbhog");
注意点,threadLocal实现主要是在ThreadLoacalMap中

//2
private final int threadLocalHashCode = nextHashCode();
//4 默认值0
private static AtomicInteger nextHashCode = new AtomicInteger();
//3步骤使用
private static final int HASH_INCREMENT = 0x61c88647;
//3
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
//key和len是外部传入 1
int i = key.threadLocalHashCode & (len-1);
可以看到每次计算哈希值的时候,都会加一次HASH_INCREMENT黄金分割点,来更好的散列数据,然后模拟该操作:代码如下
for(int i = 0; i < listThreadLocal.size(); i++){
hashCode = listThreadLocal.get(i) * HASH_INCREMENT + HASH_INCREMENT;
Integer idx = (hashCode & 127);
log.info("key的值{},idx的值{}",listThreadLocal.get(i),idx);
if(null == result[idx]){
result[idx] = listThreadLocal.get(i);
continue;
}
String idxInRes = map.get(idx);
String idxInRess = idxInRes + "->" + listThreadLocal.get(i);
map.put(idx,idxInRess);
}
进行冲突后重复值排序
//map排序
if(CollectionUtil.isEmpty(map)){
log.info("斐波那契额散列数据集:{}",JSON.toJSONString(result));
System.out.println("===》无重复数据,不需要排序");
return;
}
mapSort(map);
使用斐波那契散列算法输出结果展示:
斐波那契额散列数据集:38,15,29,22,55,86,70,64,47,32,67,7,60,85,97,95,58,46,14,83,12,72,18,96,36,20,76,59,6,33,50,30,23,42,81,31,66,71,82,61,53,84,41,45,74,63,89,77,90,16,8,37,1,62,65,99,51,78,91,39,5,57,27,56,44,13,92,25,0,24,80,3,94,26,40,34,73,35,88,2,87,11,93,54,69,68,10,17,43,48,19,9,79,21,98,52,4,28,75,49]
===》无重复数据,不需要排序
由上我们可以看到,没有重复的数据,全部比较完美的散列到不同的地方。
参考文章:
https://blog.csdn.net/qq_26327971/article/details/104757316
https://juejin.cn/post/6844903985808146439
https://juejin.cn/user/3913917126415166
斐波那契散列算法和hashMap实践的更多相关文章
- yield和python(如何生成斐波那契數列)
您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们先抛开 generator,以一个常见的编程题目来展示 yield ...
- 【Java算法學習】斐波那契數列問題-兔子產子經典問題
/** * 用遞推算法求解斐波那契數列:Fn = Fn-2 +Fn-1; */ import java.util.*; public class Fibonacci { public static v ...
- Python初学者笔记:打印出斐波那契数列的前10项
问题:斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.- ...
- 斐波那契数列—Java
斐波那契数列想必大家都知道吧,如果不知道的话,我就再啰嗦一遍, 斐波那契数列为:1 2 3 5 8 13 ...,也就是除了第一项和第二项为1以外,对于第N项,有f(N)=f(N-1)+f(N-2). ...
- 矩阵快速幂--51nod-1242斐波那契数列的第N项
斐波那契额数列的第N项 斐波那契数列的定义如下: F(0) = 0 F(1) = 1 F(n) = F(n - 1) + F(n - 2) (n >= 2) (1, 1, 2, 3, 5, 8, ...
- golang 斐波那契数
golang 斐波那契数 package main import "fmt" /* 斐波那契数,亦称之为斐波那契数列(意大利语: Successione di Fibonacci) ...
- 利用python实现二分法和斐波那契序列
利用python实现二分法:我的实现思路如下 1.判断要查找的值是否大于最大值,如果大于则直接返回False 2.判断要查找的值是否小于最小值,如果小于则直接返回False 3.如果要查找的值在最大值 ...
- 洛谷P1720 月落乌啼算钱 题解 斐波那契数列/特征方程求解
题目链接:https://www.luogu.com.cn/problem/P1720 题目描述: 给你一个公式 ,求对应的 \(F_n\) . 解题思路: 首先不难想象这是一个斐波那契数列,我们可以 ...
- 一些代码 I (斐波那契、for...else...、try和return、classmethod、统计个数)
1. 斐波那契 from itertools import islice def fib(): a, b = 0, 1 while True: yield a a, b = b, a+b print ...
- 从零开始学习PYTHON3讲义(六)for循环跟斐波那契数列
<从零开始PYTHON3>第六讲 几乎但凡接触过一点编程的人都知道for循环,在大多数语言的学习中,这也是第一个要学习的循环模式. 但是在Python中,我们把for循环放到了while循 ...
随机推荐
- 从代码到发包,一个程序全搞定!Gitea 推出软件包自托管功能 Package Registry
2022 年 7 月的最后一天,随着 Gitea 1.17.0 版本的正式发布,Gitea 开源社区推出了一项名为 Package Registry 的包管理功能,与 Gitea 代码仓库无缝集成,类 ...
- 当 SQL DELETE 邂逅 Table aliases,会擦出怎样的火花
开心一刻 晚上,女儿眼噙泪水躺在床上 女儿:你口口声声说爱我,说陪我,却天天想着骗我零花钱,你是我亲爹吗? 我:你想知道真相 女儿:想! 我:那你先给爸爸两百块钱! 环境准备 MySQL 不同版本 利 ...
- 基于electron+vue+element构建项目模板之【改造项目篇】
1.概述 开发平台OS:windows 开发平台IDE:vs code 上一篇中已完成了electron-vue项目的创建,本篇章中则介绍在此项目基础上进行取消devtools的安装.项目结构的改造. ...
- Logstash: 启动监控及集中管理-总结
Logstash: 启动监控 配置文件:logstash.yml xpack.monitoring.enabled: true xpack.monitoring.elasticsearch.usern ...
- U盘插入电脑后图标是灰色的,打开提示“请将磁盘插入驱动器”
问题描述: U盘插到win10电脑上后,U盘图标显示灰色,双击打开提示:请将磁盘插入驱动器,无法格式化,在U盘点右键/属性,显示为容量等为0. 解决办法如下: 1.首先要下载一个U盘芯片检测工具chi ...
- gitlab cicd流水线语法
流水线语法有哪些? 流水线参数列表 Keyword Description script 运行的Shell命令或脚本. image 使用docker映像. services 使用docker服务映像. ...
- 使用FastDFS打造一款高可用的分布式文件系统
FastDFS 介绍 参考: http://www.oschina.net/p/fastdfs FastDFS 是一个开源的分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文 ...
- 手把手教你使用LabVIEW人工智能视觉工具包快速实现传统Opencv算子的调用(含源码)
前言 今天我们一起来使用LabVIEW AI视觉工具包快速实现图像的滤波与增强:图像灰度处理:阈值处理与设定:二值化处理:边缘提取与特征提取等基本操作.工具包的安装与下载方法可见之前的博客. 一.图像 ...
- SpringBoot课程学习(四)
一.profile的多文档配置方式 1.profile文件方式:提供多个配置文件,每个代表一种环境 如: 1.application-dev.properties/yml 开发环境 2.applica ...
- CSS基础-关于CSS注释的添加
在 CSS 中增加注释很简单,所有被放在/*和*/分隔符之间的文本信息都被称为注释. CSS 只有一种注释,不管是多行注释还是单行注释,都必须以/*开始.以*/结束,中间加入注释内容. 1.注释放在样 ...