redis存储空间复杂度和时间复杂度的平衡

下面是一个案例:根据奖品概率计算奖品存储空间以及时间复杂度的权衡.

1. 内存占用的计算

1.1 不同精度下的内存占用

// 精度范围(rateRange)决定了数组大小rateRange=10000// 万分位 (0.0001)rateRange=100000// 十万分位 (0.00001)rateRange=1000000// 百万分位 (0.000001)

1.2 具体计算

精度rateRange数组长度int类型占用总内存
万分位10,00010,0004 bytes40 KB
十万分位100,000100,0004 bytes400 KB
百万分位1,000,0001,000,0004 bytes4 MB
千万分位10,000,00010,000,0004 bytes40 MB
亿分位100,000,000100,000,0004 bytes400 MB

1.3 实际项目中的影响

场景1:100个奖品,万分位精度

rateRange=10000awardCount=100slotsPerAward=100// 每个奖品100个槽位内存占用=10000×4bytes=40KB ✅ 可接受

场景2:100个奖品,十万分位精度

rateRange=100000awardCount=100slotsPerAward=1000// 每个奖品1000个槽位内存占用=100000×4bytes=400KB ✅ 可接受,但增长明显

场景3:100个奖品,百万分位精度

rateRange=1000000awardCount=100slotsPerAward=10000// 每个奖品10000个槽位内存占用=1000000×4bytes=4MB ⚠️ 开始需要注意

场景4:1000个奖品,十万分位精度

rateRange=100000awardCount=1000slotsPerAward=100// 每个奖品100个槽位内存占用=100000×4bytes=400KB ✅ 可接受

场景5:1000个奖品,百万分位精度

rateRange=1000000awardCount=1000slotsPerAward=1000// 每个奖品1000个槽位内存占用=1000000×4bytes=4MB ⚠️ 内存占用较大

2. O(1) vs O(logn) 内存对比

2.1 O(1) 数组算法内存占用

// 存储随机数到奖品的映射int[]awardMappingArray=newint[rateRange];// 固定大小数组// 例如:rateRange = 1000000// 内存 = 1000000 × 4 bytes = 4 MB

特点

  • 连续内存:数组是连续内存分配
  • 固定大小:一旦分配,大小固定
  • 快速访问:直接通过索引访问,O(1)时间复杂度

2.2 O(logn) 前缀和算法内存占用

// 存储奖品信息和前缀和List<StrategyAwardEntity>awards=newArrayList<>();// 奖品列表double[]prefixSums=newdouble[awardCount];// 前缀和数组// 例如:awardCount = 1000// 内存 = 1000 × (award对象大小 + 8 bytes) ≈ 100 KB

特点

  • 动态大小:根据奖品数量分配
  • 只存奖品:不存储随机数映射,只存奖品本身
  • 计算查询:每次抽奖需要计算前缀和并二分查找

2.3 内存对比表

对比项O(1) 数组算法O(logn) 前缀和算法
内存占用O(rateRange)O(awardCount)
万分位(10K奖品)40 KB~1 MB(奖品对象)
十万位(1K奖品)400 KB~100 KB
百万位(100奖品)4 MB~10 KB
关系与精度成正比与奖品数量成正比

2.4 关键发现

重要结论

rateRange >> awardCount 时,O(logn) 更节省内存 rateRange ≈ awardCount 时,两者内存相近

典型场景

万分位 + 100奖品: rateRange(10000) > awardCount(100) → O(1)更省内存 万分位 + 1万奖品: rateRange(10000) ≈ awardCount(10000) → 差不多 万分位 + 10万奖品: rateRange(10000) < awardCount(100000) → O(logn)更省内存

3. 时间和空间的权衡

3.1 Trade-off 示意图

内存占用 ↑ │ O(1)数组算法 │ / │ / │ / │ / │ / │ / │ / │ / │ / │/ └─────────────────────────────────→ 精度(rateRange) 低 中 高 非常高 O(logn)算法内存几乎不变

3.2 时间复杂度对比

操作O(1) 数组算法O(logn) 前缀和算法
预热O(rateRange)O(awardCount × log awardCount)
单次抽奖O(1)O(log awardCount)
空间O(rateRange)O(awardCount)

3.3 决策矩阵

场景rateRangeawardCount推荐算法原因
110,000100O(1)内存小(40KB),查询快
2100,000100O(1)内存可接受(400KB),查询快
31,000,000100O(1)内存较大(4MB),但奖品少
410,00010,000两者皆可内存相近,看查询频率
510,000100,000O(logn)O(1)内存太大(40MB)
6100,0001,000O(logn)O(1)内存太大(400KB)
71,000,0001,000O(logn)O(1)内存太大(4MB)

4. 实际代码中的体现

4.1 O(1) 算法实现(固定数组)

/** * O(1)抽奖算法 - 预热时生成固定大小数组 * 优点:查询时间O(1) * 缺点:内存占用与rateRange成正比 */publicIntegerraffleStrategyO1(LongstrategyId,List<StrategyAwardEntity>strategyAwardEntities){// 1. 获取精度范围BigDecimalminAwardRate=minAwardRate(strategyAwardEntities);intrateRange=convert(minAwardRate);// 例如:10000, 100000, 1000000// 2. 分配数组(内存占用 = rateRange × 4 bytes)int[]strategyAwardRateRandom=newint[rateRange];// 3. 填充数组intcurrentIndex=0;for(StrategyAwardEntityaward:strategyAwardEntities){// 计算该奖品应该占用的槽位数intawardSlots=(int)(award.getAwardRate()*rateRange);// 填充槽位for(inti=0;i<awardSlots;i++){if(currentIndex>=rateRange)break;strategyAwardAwardRateRandom[currentIndex++]=award.getAwardId();}}// 4. 随机打乱(消除初始顺序偏差)shuffle(strategyAwardAwardRateRandom);// 5. 抽奖(O(1)时间复杂度)intrandomIndex=ThreadLocalRandom.current().nextInt(rateRange);returnstrategyAwardAwardRateRandom[randomIndex];}

4.2 O(logn) 算法实现(前缀和)

/** * O(logn)抽奖算法 - 实时计算前缀和 * 优点:内存占用与awardCount成正比,与rateRange无关 * 缺点:查询时间O(logn),需要每次计算 */publicIntegerraffleStrategyLogn(LongstrategyId,List<StrategyAwardEntity>strategyAwardEntities){// 1. 计算最小精度(用于确定随机数范围)BigDecimalminAwardRate=minAwardRate(strategyAwardEntities);intrateRange=convert(minAwardRate);// 例如:100000, 1000000// 2. 构建前缀和数组(内存占用 = awardCount × 8 bytes)double[]awardRates=newdouble[strategyAwardEntities.size()];double[]prefixSums=newdouble[strategyAwardEntities.size()];for(inti=0;i<strategyAwardEntities.size();i++){StrategyAwardEntityaward=strategyAwardEntities.get(i);awardRates[i]=award.getAwardRate();if(i==0){prefixSums[i]=awardRates[i];}else{prefixSums[i]=prefixSums[i-1]+awardRates[i];}}// 3. 生成随机数intrandomValue=ThreadLocalRandom.current().nextInt(rateRange);doublerandomRate=(double)randomValue/rateRange;// 4. 二分查找(O(logn)时间复杂度)intleft=0;intright=prefixSums.length-1;while(left<right){intmid=(left+right)/2;if(prefixSums[mid]<randomRate){left=mid+1;}else{right=mid;}}returnstrategyAwardEntities.get(left).getAwardId();}

4.3 算法选择(综合考虑)

/** * 综合考虑槽位数和内存占用的算法选择 */publicIntegerraffleStrategy(LongstrategyId,List<StrategyAwardEntity>strategyAwardEntities){// 1. 计算精度范围BigDecimalminAwardRate=minAwardRate(strategyAwardEntities);intrateRange=convert(minAwardRate);// 10000, 100000, 1000000...intawardCount=strategyAwardEntities.size();intslotsPerAward=rateRange/awardCount;// 2. 计算内存占用longo1MemoryBytes=(long)rateRange*4;// O(1)数组内存longlognMemoryBytes=(long)awardCount*40;// O(logn)奖品对象内存估算// 3. 算法选择条件// 条件1:槽位数 >= 10(保证公平性)// 条件2:内存占用可接受(O(1)算法不超过10MB)if(slotsPerAward>=10&&o1MemoryBytes<=10*1024*1024){// 使用O(1)算法returnraffleStrategyO1(strategyId,strategyAwardEntities);}else{// 使用O(logn)算法returnraffleStrategyLogn(strategyId,strategyAwardEntities);}}

5. 内存优化方案

5.1 压缩存储

/** * 内存优化:如果rateRange非常大,使用压缩算法 */publicIntegerraffleStrategyCompressed(LongstrategyId,List<StrategyAwardEntity>strategyAwardEntities){BigDecimalminAwardRate=minAwardRate(strategyAwardEntities);intrateRange=convert(minAwardRate);// 如果rateRange > 1000000,使用Run-Length Encoding压缩if(rateRange>1000000){returnraffleStrategyCompressed(strategyId,strategyAwardEntities);}// 正常O(1)算法returnraffleStrategyO1(strategyId,strategyAwardEntities);}/** * Run-Length Encoding压缩存储 * 例如:[A,A,A,B,B,C,C,C,C] → [(A,3), (B,2), (C,4)] */classCompressedArray{int[]awardIds;// 奖品ID数组(去重后的)int[]runLengths;// 每个奖品连续出现的次数// 随机抽奖publicintraffle(){// 1. 计算总长度inttotalLength=Arrays.stream(runLengths).sum();// 2. 生成随机位置intrandomPosition=ThreadLocalRandom.current().nextInt(totalLength);// 3. 查找对应的奖品intcurrentPosition=0;for(inti=0;i<runLengths.length;i++){currentPosition+=runLengths[i];if(randomPosition<currentPosition){returnawardIds[i];}}returnawardIds[awardIds.length-1];}}

5.2 分级缓存

/** * 分级缓存策略:根据访问频率选择不同的存储方式 */publicclassTieredStrategyCache{// 热数据:高频访问的奖品,使用O(1)数组privateint[]hotAwardsArray;// 冷数据:低频访问的奖品,使用O(logn)列表privateList<StrategyAwardEntity>coldAwardsList;// 访问统计privateMap<Long,AtomicInteger>accessCountMap=newConcurrentHashMap<>();// 定期调整热冷数据publicvoidrebalance(){// 统计访问频率Map<Long,Integer>sortedAwards=accessCountMap.entrySet().stream().sorted(Map.Entry.comparingByValue()).limit(100)// 取访问最多的100个奖品.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(e1,e2)->e1,LinkedHashMap::new));// 将高频奖品放入热数据rebuildHotAwardsArray(sortedAwards.keySet());}}

5.3 懒加载

/** * 懒加载策略:只有首次访问时才加载数据 */publicclassLazyStrategyCache{privatevolatileint[]cachedArray=null;privatevolatileList<StrategyAwardEntity>cachedAwards=null;// 懒加载O(1)数组publicint[]getO1Array(List<StrategyAwardEntity>awards){if(cachedArray==null){synchronized(this){if(cachedArray==null){// 只在首次访问时计算cachedArray=buildStrategyArray(awards);}}}returncachedArray;}// 懒加载O(logn)列表publicList<StrategyAwardEntity>getAwardsList(){if(cachedAwards==null){synchronized(this){if(cachedAwards==null){cachedAwards=loadAwardsFromDB();}}}returncachedAwards;}}

6. 总结

您提出的问题非常关键,总结几点:

6.1 内存占用的核心问题

O(1)算法内存 = rateRange × 4 bytes rateRange越大,内存占用越大 O(logn)算法内存 = awardCount × 奖品对象大小 与rateRange无关,只与奖品数量有关

6.2 算法选择的影响因素

因素O(1)算法O(logn)算法
内存与精度成正比与奖品数量成正比
时间O(1)快O(logn)稍慢
公平性依赖槽位数实时计算,更公平
复杂度实现简单需要前缀和+二分查找

6.3 最佳实践

  1. 小精度(万分位):优先使用O(1),内存小(40KB),查询快

  2. 大精度(十万分位+)

    • 奖品数量少(100以内) → O(1)可接受
    • 奖品数量多(1000以上) → O(logn)更省内存
  3. 超高精度(百万分位+):建议使用O(logn),内存更可控

6.4 内存优化建议

  1. 压缩存储:使用RLE等压缩算法
  2. 分级缓存:热数据用O(1),冷数据用O(logn)
  3. 懒加载:首次访问时再加载
  4. 动态调整:根据实际运行情况选择算法

这就是为什么算法选择需要综合考虑时间复杂度、空间复杂度、公平性等多个因素的原因。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1124886.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CPU密集型任务与I/O密集型任务详解

目录 CPU密集型任务与I/O密集型任务详解 一、基本概念 CPU密集型任务&#xff08;CPU-bound&#xff09; I/O密集型任务&#xff08;I/O-bound&#xff09; 二、关键区别对比 三、详细特征与应用场景 CPU密集型任务 I/O密集型任务 四、线程池配置建议 CPU密集型任务 …

bss段、data段、text段、heap堆、stack栈的基本概念

1. bss段bss段&#xff08;bss segment&#xff09;通常是指用来存放程序中未初始化的全局变量的一块内存区域。bss是英文Block Started by Symbol的简称。bss段属于静态内存分配。2. data段数据段&#xff08;data segment&#xff09;通常是指用来存放程序中已初始化的全局变…

【SRC 挖洞赚钱实测】单高危漏洞 2W+!低中高危赏金拆解,挖漏洞平均日收入算给你看

一个漏洞奖励2w&#xff0c;这是真实的嘛&#xff01; 我入行网安这些年也一直在接私活&#xff0c;副业赚的钱几乎是我工资的三倍&#xff01;看到最近副业挖漏洞的内容非常火爆&#xff0c;我便决定将自己的经验分享出来&#xff0c;带我的粉丝们一起挣钱&#xff01; 注意…

破防了!Java 卷到月薪 8K,转网安直接年薪 40 万 +,2025 自救路线图全公开!

从卷Java到冲网安&#xff1a;计算机人2025自救路线图&#xff08;附安全岗年薪40-150万&#xff09; 只要你敢学我就敢教&#xff01;500集黑客入狱教程&#xff0c;从入门到入狱&#xff01;全程干货无废话&#xff0c;学不会我退网&#xff01; 计算机专业未来发展全景图&…

抗干扰更强!8路PWM输出模块:隔离RS485/CAN通讯+占空比精准调节

8路PWM脉冲输出模块&#xff0c;是一种集成化的工业控制/电子接口模块&#xff0c;其核心功能是同时提供8组独立或可同步的、可编程的PWM(脉冲宽度调制)脉冲信号输出。通过内置的控制核心、定时单元和功率驱动电路&#xff0c;将外部控制器(PLC、工控机、单片机、云平台)的指令…

多元场景通吃的秘密!8路PWM输出模块——集中控制+独立调节,双重模式更灵活

8路PWM脉冲输出模块的核心价值在于多通道独立/同步的占空比、频率可调控制&#xff0c;能同时驱动8路执行器实现调速、调光、调压、调力等精准控制。结合工业自动化、物联网、智能装备等领域的实际需求&#xff0c;其典型应用场景可分为以下8大类&#xff0c;覆盖从工业核心设备…

副业封神!挖 SRC 漏洞一个就赚 2W+,网安人轻松躺赚,平均日收入看完馋哭了!

一个漏洞奖励2w&#xff0c;这是真实的嘛&#xff01; 我入行网安这些年也一直在接私活&#xff0c;副业赚的钱几乎是我工资的三倍&#xff01;看到最近副业挖漏洞的内容非常火爆&#xff0c;我便决定将自己的经验分享出来&#xff0c;带我的粉丝们一起挣钱&#xff01; 注意…

2026年六大GEO优化公司能力测评:技术纵深决定AI搜索霸权

2026年&#xff0c;生成式AI已占据用户决策链的核心节点。QuestMobile数据显示&#xff0c;中国AI搜索用户规模突破4.2亿&#xff0c;用户日均通过DeepSeek、文心一言、Kimi等平台发起超过2.8亿次商业类提问。当"新能源车怎么选""智能家居品牌推荐"这类问题…

在Windows上编译、安装Rust

你想知道在Windows系统上安装Rust编程语言的具体方法&#xff0c;我会为你提供官方推荐、新手友好的完整步骤&#xff0c;包括前置依赖、安装操作和验证方法&#xff0c;确保你能顺利完成安装。 一、前置准备&#xff08;关键&#xff01;避免后续编译报错&#xff09; Rust在W…

救命!失业党狂喜!零成本学网安 3 个月,避开万元培训班,月薪 12K 成功逆袭!

失业 3 个月投了 127 份简历&#xff1f;别卷了&#xff01;我靠网安转行月薪 12K&#xff0c;附 3 个月零成本入门攻略 去年被裁那天&#xff0c;我盯着招聘软件上 “35 岁以下优先” 的字样&#xff0c;把简历里的 “5 年行政经验” 改了又改&#xff0c;结果投出去的 127 份…

艾莉丝努力练剑的256天创作纪念日:在代码星河中逐梦前行

&#x1f525;艾莉丝努力练剑&#xff1a;个人主页 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》、C/C干货分享&学习过程记录、Linux操作系统编程详解、笔试/面试常见算法&#xff1a;从基础到进阶、测试开发要点全知道 ⭐️为天地立心&#xff0c;为生民立命…

pycocotools绘制标注结果(bbox,segment,pose)

博主提供docker开发镜像(主要面向深度学习、计算机视觉和机器人开发等需要 GPU 支持的场景)用于测试所有博客用例代码&#xff0c;下载地址(ubuntu24_cux_dev)[https://wilson.blog.csdn.net/article/details/156309082] 以下是一个基于 pycocotools 开发的 Python 函数&#x…

深度测评9个AI论文工具,助本科生轻松搞定毕业论文!

深度测评9个AI论文工具&#xff0c;助本科生轻松搞定毕业论文&#xff01; AI 工具如何改变论文写作的未来 随着人工智能技术的飞速发展&#xff0c;越来越多的本科生开始借助 AI 工具来辅助完成毕业论文。这些工具不仅能够帮助学生高效地完成内容生成、结构搭建&#xff0c;还…

2025 Java 转网安必看:从技能迁移到岗位选择,附 40-150 万安全岗薪资拆解!

从卷Java到冲网安&#xff1a;计算机人2025自救路线图&#xff08;附安全岗年薪40-150万&#xff09; 只要你敢学我就敢教&#xff01;500集黑客入狱教程&#xff0c;从入门到入狱&#xff01;全程干货无废话&#xff0c;学不会我退网&#xff01; 计算机专业未来发展全景图&…

从0到1讲解什么是JVM:Java程序员必须掌握的核心知识(附Spring Boot实战案例)

视频看了几百小时还迷糊&#xff1f;关注我&#xff0c;几分钟让你秒懂&#xff01;一、什么是JVM&#xff1f;小白也能听懂的解释JVM&#xff08;Java Virtual Machine&#xff09;&#xff0c;中文叫 Java虚拟机&#xff0c;是运行Java程序的“发动机”。 你写的 .java 文件&…

满足成本与性能双重要求:AWS中为访问私有子网EC2的Lambda实施优化方案

一家公司使用 Amazon EC2 实例和 AWS Lambda 函数来运行其应用程序。该公司在其 AWS 账户中拥有包含公共子网和私有子网的 VPC。EC2 实例在其中一个 VPC 的私有子网中运行。Lambda 函数需要直接网络访问 EC2 实例以使应用程序工作。 该应用程序将运行至少 1 年。公司预计在此期…

网安小白狂喜!Web 安全入门指南:OWASP Top 10 + 漏洞实战,收藏这篇就够了!

Web 安全入门&#xff1a;从 OWASP Top 10 到常见漏洞 解构 Web 安全威胁图谱&#xff1a;从 OWASP Top 10 到典型攻击&#xff0c;筑牢数字防线 在 Web 应用成为业务核心载体的今天&#xff0c;安全漏洞已成为威胁数据隐私、业务稳定的 “隐形炸弹”。从 OWASP Top 10 划定的…

Spring循环依赖:原理、限制与解决方案深度解析

一、循环依赖的定义与本质在Spring框架中&#xff0c;循环依赖指的是两个或多个Bean之间存在直接或间接的相互引用关系&#xff0c;从而形成一个闭合的依赖环。简而言之&#xff0c;当BeanA依赖BeanB&#xff0c;同时BeanB也依赖BeanA时&#xff0c;便构成了典型的循环依赖。代…

并发编程中的CAS机制:原理、实现与应用剖析

在并发编程领域&#xff0c;确保线程安全通常首先会联想到加锁机制&#xff0c;如synchronized或ReentrantLock。虽然加锁是直观且广泛采用的方案&#xff0c;但在高并发场景下&#xff0c;锁带来的性能开销——如上下文切换、内核态切换及线程阻塞——可能成为系统瓶颈。为此&…

挖到宝了!专本科网安自学指南:不看学历,少走 2 年弯路,评论区蹲资料的来!

目录 前言自学网安第一阶段&#xff1a;打牢基础 学习这些基础知识有什么用呢&#xff1f; 第二阶段&#xff1a;化整为零 学习建议 第三阶段&#xff1a;实战演练 实践技巧 第四阶段&#xff1a;找准定位 深入学习建议学习要避开的弯路自学失败的原因有很多 最后&#xff1a;…