2026/4/3 3:04:31
网站建设
项目流程
短视频免费素材网站,建设网站需要哪些语言,怎么做好手机网站开发,关于我们页面模板下面是一个案例:根据奖品概率计算奖品存储空间以及时间复杂度的权衡.
1. 内存占用的计算
1.1 不同精度下的内存占用
// 精度范围#xff08;rateRange#xff09;决定了数组大小
rateRange 10000 // 万分位 (0.0001)
rateRange 100000 // 十万分位 (0.00001)
r…下面是一个案例:根据奖品概率计算奖品存储空间以及时间复杂度的权衡.1. 内存占用的计算1.1 不同精度下的内存占用// 精度范围rateRange决定了数组大小rateRange10000// 万分位 (0.0001)rateRange100000// 十万分位 (0.00001)rateRange1000000// 百万分位 (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 MB1.3 实际项目中的影响场景1100个奖品万分位精度rateRange10000awardCount100slotsPerAward100// 每个奖品100个槽位内存占用10000×4bytes40KB ✅ 可接受场景2100个奖品十万分位精度rateRange100000awardCount100slotsPerAward1000// 每个奖品1000个槽位内存占用100000×4bytes400KB ✅ 可接受但增长明显场景3100个奖品百万分位精度rateRange1000000awardCount100slotsPerAward10000// 每个奖品10000个槽位内存占用1000000×4bytes4MB ⚠️ 开始需要注意场景41000个奖品十万分位精度rateRange100000awardCount1000slotsPerAward100// 每个奖品100个槽位内存占用100000×4bytes400KB ✅ 可接受场景51000个奖品百万分位精度rateRange1000000awardCount1000slotsPerAward1000// 每个奖品1000个槽位内存占用1000000×4bytes4MB ⚠️ 内存占用较大2. O(1) vs O(logn) 内存对比2.1 O(1) 数组算法内存占用// 存储随机数到奖品的映射int[]awardMappingArraynewint[rateRange];// 固定大小数组// 例如rateRange 1000000// 内存 1000000 × 4 bytes 4 MB特点连续内存数组是连续内存分配固定大小一旦分配大小固定快速访问直接通过索引访问O(1)时间复杂度2.2 O(logn) 前缀和算法内存占用// 存储奖品信息和前缀和ListStrategyAwardEntityawardsnewArrayList();// 奖品列表double[]prefixSumsnewdouble[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,ListStrategyAwardEntitystrategyAwardEntities){// 1. 获取精度范围BigDecimalminAwardRateminAwardRate(strategyAwardEntities);intrateRangeconvert(minAwardRate);// 例如10000, 100000, 1000000// 2. 分配数组内存占用 rateRange × 4 bytesint[]strategyAwardRateRandomnewint[rateRange];// 3. 填充数组intcurrentIndex0;for(StrategyAwardEntityaward:strategyAwardEntities){// 计算该奖品应该占用的槽位数intawardSlots(int)(award.getAwardRate()*rateRange);// 填充槽位for(inti0;iawardSlots;i){if(currentIndexrateRange)break;strategyAwardAwardRateRandom[currentIndex]award.getAwardId();}}// 4. 随机打乱消除初始顺序偏差shuffle(strategyAwardAwardRateRandom);// 5. 抽奖O(1)时间复杂度intrandomIndexThreadLocalRandom.current().nextInt(rateRange);returnstrategyAwardAwardRateRandom[randomIndex];}4.2 O(logn) 算法实现前缀和/** * O(logn)抽奖算法 - 实时计算前缀和 * 优点内存占用与awardCount成正比与rateRange无关 * 缺点查询时间O(logn)需要每次计算 */publicIntegerraffleStrategyLogn(LongstrategyId,ListStrategyAwardEntitystrategyAwardEntities){// 1. 计算最小精度用于确定随机数范围BigDecimalminAwardRateminAwardRate(strategyAwardEntities);intrateRangeconvert(minAwardRate);// 例如100000, 1000000// 2. 构建前缀和数组内存占用 awardCount × 8 bytesdouble[]awardRatesnewdouble[strategyAwardEntities.size()];double[]prefixSumsnewdouble[strategyAwardEntities.size()];for(inti0;istrategyAwardEntities.size();i){StrategyAwardEntityawardstrategyAwardEntities.get(i);awardRates[i]award.getAwardRate();if(i0){prefixSums[i]awardRates[i];}else{prefixSums[i]prefixSums[i-1]awardRates[i];}}// 3. 生成随机数intrandomValueThreadLocalRandom.current().nextInt(rateRange);doublerandomRate(double)randomValue/rateRange;// 4. 二分查找O(logn)时间复杂度intleft0;intrightprefixSums.length-1;while(leftright){intmid(leftright)/2;if(prefixSums[mid]randomRate){leftmid1;}else{rightmid;}}returnstrategyAwardEntities.get(left).getAwardId();}4.3 算法选择综合考虑/** * 综合考虑槽位数和内存占用的算法选择 */publicIntegerraffleStrategy(LongstrategyId,ListStrategyAwardEntitystrategyAwardEntities){// 1. 计算精度范围BigDecimalminAwardRateminAwardRate(strategyAwardEntities);intrateRangeconvert(minAwardRate);// 10000, 100000, 1000000...intawardCountstrategyAwardEntities.size();intslotsPerAwardrateRange/awardCount;// 2. 计算内存占用longo1MemoryBytes(long)rateRange*4;// O(1)数组内存longlognMemoryBytes(long)awardCount*40;// O(logn)奖品对象内存估算// 3. 算法选择条件// 条件1槽位数 10保证公平性// 条件2内存占用可接受O(1)算法不超过10MBif(slotsPerAward10o1MemoryBytes10*1024*1024){// 使用O(1)算法returnraffleStrategyO1(strategyId,strategyAwardEntities);}else{// 使用O(logn)算法returnraffleStrategyLogn(strategyId,strategyAwardEntities);}}5. 内存优化方案5.1 压缩存储/** * 内存优化如果rateRange非常大使用压缩算法 */publicIntegerraffleStrategyCompressed(LongstrategyId,ListStrategyAwardEntitystrategyAwardEntities){BigDecimalminAwardRateminAwardRate(strategyAwardEntities);intrateRangeconvert(minAwardRate);// 如果rateRange 1000000使用Run-Length Encoding压缩if(rateRange1000000){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. 计算总长度inttotalLengthArrays.stream(runLengths).sum();// 2. 生成随机位置intrandomPositionThreadLocalRandom.current().nextInt(totalLength);// 3. 查找对应的奖品intcurrentPosition0;for(inti0;irunLengths.length;i){currentPositionrunLengths[i];if(randomPositioncurrentPosition){returnawardIds[i];}}returnawardIds[awardIds.length-1];}}5.2 分级缓存/** * 分级缓存策略根据访问频率选择不同的存储方式 */publicclassTieredStrategyCache{// 热数据高频访问的奖品使用O(1)数组privateint[]hotAwardsArray;// 冷数据低频访问的奖品使用O(logn)列表privateListStrategyAwardEntitycoldAwardsList;// 访问统计privateMapLong,AtomicIntegeraccessCountMapnewConcurrentHashMap();// 定期调整热冷数据publicvoidrebalance(){// 统计访问频率MapLong,IntegersortedAwardsaccessCountMap.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[]cachedArraynull;privatevolatileListStrategyAwardEntitycachedAwardsnull;// 懒加载O(1)数组publicint[]getO1Array(ListStrategyAwardEntityawards){if(cachedArraynull){synchronized(this){if(cachedArraynull){// 只在首次访问时计算cachedArraybuildStrategyArray(awards);}}}returncachedArray;}// 懒加载O(logn)列表publicListStrategyAwardEntitygetAwardsList(){if(cachedAwardsnull){synchronized(this){if(cachedAwardsnull){cachedAwardsloadAwardsFromDB();}}}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 最佳实践小精度万分位优先使用O(1)内存小(40KB)查询快大精度十万分位奖品数量少(100以内) → O(1)可接受奖品数量多(1000以上) → O(logn)更省内存超高精度百万分位建议使用O(logn)内存更可控6.4 内存优化建议压缩存储使用RLE等压缩算法分级缓存热数据用O(1)冷数据用O(logn)懒加载首次访问时再加载动态调整根据实际运行情况选择算法这就是为什么算法选择需要综合考虑时间复杂度、空间复杂度、公平性等多个因素的原因。