如何高效生成10万个无重复的3D空间Vector3随机点?
高效生成无重复且带聚类效果的3D Vector3点
嘿,这个需求挺实际的!直接像你原代码那样纯随机生成,大概率会出现重复点,而且后期生成重复的概率越来越高,效率极低。要同时满足「10万个无重复点」和「聚类效果」,咱们可以从避免重复生成和定向聚类两个方向入手,下面给你具体的思路和代码示例:
核心思路拆解
1. 高效去重:别生成后再去重,要从源头避免
直接生成后遍历列表去重的时间复杂度是O(n²),10万个点的话会慢到离谱。最优方案是用**哈希集合(HashSet)**记录已经生成的点的唯一标识,每次生成新点先查集合,O(1)的查询速度能把效率拉满。
这里要注意浮点数的精度问题:比如0.1和0.100000001其实是同一个位置(对咱们的需求来说),但直接比较浮点数会被当成不同点。解决办法是把坐标放大转成整数,再生成唯一键(比如字符串),这样就能精准判断重复。
2. 聚类效果:多中心点+偏移生成
要让点呈现聚类效果,不用搞复杂的算法,直接选几个「中心点」,围绕每个中心点生成带随机偏移的点就行:
- 中心点数量决定聚类的数量(比如8-10个,越多聚类越分散)
- 偏移范围决定聚类的紧密程度(范围越小,点越集中在中心点周围)
- 还可以给不同中心点分配不同数量的点,让聚类大小不一样,更自然。
具体代码实现(Java)
下面是修改后的完整代码,包含去重逻辑和聚类生成,你可以直接用:
import java.util.ArrayList; import java.util.HashSet; import java.util.Random; public class Vector3 { public float x; public float y; public float z; Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } // 生成唯一键:解决浮点数精度问题,缩放倍数可按需调整 private String getUniqueKey() { int scale = 1000; // 精度到0.001,足够大多数场景使用 return String.format("%d_%d_%d", Math.round(x * scale), Math.round(y * scale), Math.round(z * scale)); } /** * 生成带聚类效果的无重复3D点 * @param totalPoints 总点数(比如100000) * @param minX/X/Y/Z 3D空间的边界 * @return 无重复的Vector3列表 */ public static ArrayList<Vector3> generateClusteredUniqueVectors( int totalPoints, float minX, float maxX, float minY, float maxY, float minZ, float maxZ) { ArrayList<Vector3> resultList = new ArrayList<>(); HashSet<String> usedKeys = new HashSet<>(); Random random = new Random(); // 第一步:生成聚类中心点(数量可调整) int clusterCount = 8; Vector3[] clusterCenters = new Vector3[clusterCount]; for (int i = 0; i < clusterCount; i++) { float cx = minX + random.nextFloat() * (maxX - minX); float cy = minY + random.nextFloat() * (maxY - minY); float cz = minZ + random.nextFloat() * (maxZ - minZ); clusterCenters[i] = new Vector3(cx, cy, cz); } // 第二步:给每个聚类分配点数(随机分配,保证总和是totalPoints) int[] pointsPerCluster = new int[clusterCount]; int remainingPoints = totalPoints; for (int i = 0; i < clusterCount - 1; i++) { // 每个聚类至少1个点,最多分剩下的一半 pointsPerCluster[i] = 1 + random.nextInt(remainingPoints - (clusterCount - i - 1)); remainingPoints -= pointsPerCluster[i]; } pointsPerCluster[clusterCount - 1] = remainingPoints; // 第三步:围绕每个中心点生成点 for (int i = 0; i < clusterCount; i++) { Vector3 center = clusterCenters[i]; int needGenerate = pointsPerCluster[i]; // 聚类偏移范围:比如边界的1/10,越小聚类越集中 float offsetX = (maxX - minX) * 0.1f; float offsetY = (maxY - minY) * 0.1f; float offsetZ = (maxZ - minZ) * 0.1f; while (needGenerate > 0) { // 生成中心点附近的随机点 float x = center.x + (random.nextFloat() * 2 - 1) * offsetX; float y = center.y + (random.nextFloat() * 2 - 1) * offsetY; float z = center.z + (random.nextFloat() * 2 - 1) * offsetZ; // 确保点在边界内 x = Math.max(minX, Math.min(maxX, x)); y = Math.max(minY, Math.min(maxY, y)); z = Math.max(minZ, Math.min(maxZ, z)); Vector3 newVec = new Vector3(x, y, z); String key = newVec.getUniqueKey(); if (!usedKeys.contains(key)) { usedKeys.add(key); resultList.add(newVec); needGenerate--; } } } return resultList; } // 测试用例 public static void main(String[] args) { ArrayList<Vector3> vectors = generateClusteredUniqueVectors( 100000, 0, 100, 0, 100, 0, 100); System.out.println("成功生成点数:" + vectors.size()); } }
更高效的进阶方案:基于网格的生成
如果你的3D空间足够大(比如能划分出至少10万个不重叠的小网格),可以用网格离散化的方式,完全避免重复点的生成,效率更高:
// 基于网格的高效生成:每个点来自不同网格单元,绝对无重复 public static ArrayList<Vector3> generateGridBasedClusteredVectors( int totalPoints, float minX, float maxX, float minY, float maxY, float minZ, float maxZ, float gridSize) { ArrayList<Vector3> resultList = new ArrayList<>(); HashSet<String> usedGrids = new HashSet<>(); Random random = new Random(); // 计算网格数量 int gridX = (int) Math.ceil((maxX - minX) / gridSize); int gridY = (int) Math.ceil((maxY - minY) / gridSize); int gridZ = (int) Math.ceil((maxZ - minZ) / gridSize); // 生成聚类中心点对应的网格 int clusterCount = 8; ArrayList<int[]> clusterGrids = new ArrayList<>(); for (int i = 0; i < clusterCount; i++) { int gx = random.nextInt(gridX); int gy = random.nextInt(gridY); int gz = random.nextInt(gridZ); clusterGrids.add(new int[]{gx, gy, gz}); } // 分配每个聚类的点数 int[] pointsPerCluster = new int[clusterCount]; int remainingPoints = totalPoints; for (int i = 0; i < clusterCount - 1; i++) { pointsPerCluster[i] = 1 + random.nextInt(remainingPoints - (clusterCount - i - 1)); remainingPoints -= pointsPerCluster[i]; } pointsPerCluster[clusterCount - 1] = remainingPoints; // 生成每个聚类的点:围绕中心点网格的附近网格 for (int i = 0; i < clusterCount; i++) { int[] centerGrid = clusterGrids.get(i); int needGenerate = pointsPerCluster[i]; int gridOffset = 2; // 聚类覆盖的网格范围,越大越分散 while (needGenerate > 0) { // 在中心点网格的偏移范围内选网格 int gx = centerGrid[0] + random.nextInt(gridOffset * 2 + 1) - gridOffset; int gy = centerGrid[1] + random.nextInt(gridOffset * 2 + 1) - gridOffset; int gz = centerGrid[2] + random.nextInt(gridOffset * 2 + 1) - gridOffset; // 跳过超出边界的网格 if (gx < 0 || gx >= gridX || gy < 0 || gy >= gridY || gz < 0 || gz >= gridZ) { continue; } String gridKey = gx + "_" + gy + "_" + gz; if (!usedGrids.contains(gridKey)) { usedGrids.add(gridKey); // 在网格内随机生成点 float x = minX + gx * gridSize + random.nextFloat() * gridSize; float y = minY + gy * gridSize + random.nextFloat() * gridSize; float z = minZ + gz * gridSize + random.nextFloat() * gridSize; resultList.add(new Vector3(x, y, z)); needGenerate--; } } } return resultList; }
这种方式的好处是完全不会有重复点,因为每个点都来自独立的网格单元,生成速度极快,适合对效率要求极高的场景。
最后调整建议
- 聚类数量:修改
clusterCount的值,越多聚类越分散 - 聚类紧密程度:调整
offsetX/Y/Z的系数(比如改成0.05,聚类会更集中) - 精度控制:修改
getUniqueKey里的scale值,比如要更高精度就用10000
内容的提问来源于stack exchange,提问作者Hyperegg
相关产品推荐
相关产品推荐

