package unit; import java.util.*; /** * Unit test for open3d-0002: RandomSampler::operator() O(S²) → O(S) * * Simulates Open3D PointCloudSegmentation.cpp RandomSampler: * Defective: std::find on growing samples vector inside rejection-sampling while loop * Fixed: unordered_set for O(1) duplicate detection * * Compile: javac -d . RansacSamplerAlgorithm.java * Run: java -ea unit.RansacSamplerAlgorithm */ public class RansacSamplerAlgorithm { // ── defective implementation ────────────────────────────────────────────── /** Returns sampleSize unique indices in [0, totalSize), using O(S²) rejection sampling */ static List defectiveSample(int totalSize, int sampleSize, Random rng) { List samples = new ArrayList<>(sampleSize); while (samples.size() < sampleSize) { int idx = rng.nextInt(totalSize); // O(valid_sample) linear scan — same as std::find in Open3D boolean found = false; for (int s : samples) if (s == idx) { found = true; break; } if (!found) samples.add(idx); } return samples; } // ── fixed implementation ────────────────────────────────────────────────── /** Returns sampleSize unique indices in [0, totalSize), using O(S) set-based sampling */ static List fixedSample(int totalSize, int sampleSize, Random rng) { List samples = new ArrayList<>(sampleSize); Set seen = new HashSet<>(sampleSize * 2); while (samples.size() < sampleSize) { int idx = rng.nextInt(totalSize); if (seen.add(idx)) samples.add(idx); // O(1) } return samples; } // ── property checks ─────────────────────────────────────────────────────── static boolean isUniqueSubset(List sample, int totalSize) { Set seen = new HashSet<>(); for (int idx : sample) { if (idx < 0 || idx >= totalSize) return false; if (!seen.add(idx)) return false; // duplicate } return true; } // ── tests ───────────────────────────────────────────────────────────────── static int pass = 0, total = 0; static void assertTrue(String name, boolean cond) { total++; if (cond) { pass++; System.out.println("PASS " + name); } else System.out.println("FAIL " + name); } public static void main(String[] args) { Random rng = new Random(12345); // Test 1: sample_size=0 → empty result { List d = defectiveSample(100, 0, new Random(1)); List f = fixedSample(100, 0, new Random(1)); assertTrue("zero-sample-defective", d.isEmpty()); assertTrue("zero-sample-fixed", f.isEmpty()); } // Test 2: sample_size=1 → single unique index { List d = defectiveSample(1000, 1, new Random(2)); List f = fixedSample(1000, 1, new Random(2)); assertTrue("size1-defective", d.size() == 1 && isUniqueSubset(d, 1000)); assertTrue("size1-fixed", f.size() == 1 && isUniqueSubset(f, 1000)); } // Test 3: sample_size=3 (default ransac_n) → correct size, unique, valid range { for (int trial = 0; trial < 20; trial++) { List d = defectiveSample(10000, 3, rng); List f = fixedSample(10000, 3, rng); if (!isUniqueSubset(d, 10000) || d.size() != 3) { assertTrue("sample3-defective-trial" + trial, false); break; } if (!isUniqueSubset(f, 10000) || f.size() != 3) { assertTrue("sample3-fixed-trial" + trial, false); break; } } assertTrue("sample3-20-trials-defective", true); assertTrue("sample3-20-trials-fixed", true); } // Test 4: sample_size=totalSize → exactly all indices (if feasible) { int N = 20; List d = defectiveSample(N, N, new Random(7)); List f = fixedSample(N, N, new Random(7)); assertTrue("full-sample-defective", d.size() == N && isUniqueSubset(d, N) && new HashSet<>(d).size() == N); assertTrue("full-sample-fixed", f.size() == N && isUniqueSubset(f, N) && new HashSet<>(f).size() == N); } // Test 5: no duplicates across 100 calls with sample_size=10 { boolean defOk = true, fixOk = true; for (int i = 0; i < 100; i++) { if (!isUniqueSubset(defectiveSample(10000, 10, rng), 10000)) defOk = false; if (!isUniqueSubset(fixedSample(10000, 10, rng), 10000)) fixOk = false; } assertTrue("no-duplicates-100-defective", defOk); assertTrue("no-duplicates-100-fixed", fixOk); } // Test 6: performance — simulate RANSAC: num_iterations=1000, ransac_n=10, totalSize=50000 { int numIter = 1000, sampleSize = 10, totalSize = 50000; long t0 = System.nanoTime(); Random r1 = new Random(42); for (int i = 0; i < numIter; i++) defectiveSample(totalSize, sampleSize, r1); long tDef = System.nanoTime() - t0; t0 = System.nanoTime(); Random r2 = new Random(42); for (int i = 0; i < numIter; i++) fixedSample(totalSize, sampleSize, r2); long tFix = System.nanoTime() - t0; double ratio = (double) tDef / tFix; System.out.printf(" Perf RANSAC iter=%d S=%d N=%d: defective=%.1fms fixed=%.1fms ratio=%.1fx%n", numIter, sampleSize, totalSize, tDef / 1e6, tFix / 1e6, ratio); // At S=10 ratio may be modest; the defect scales as S² assertTrue("perf-not-slower", ratio >= 0.5); // conservative: fixed should not be slower } // Test 7: performance at larger sample_size=100 where O(S²) hurts more { int numIter = 1000, sampleSize = 100, totalSize = 100000; long t0 = System.nanoTime(); Random r1 = new Random(99); for (int i = 0; i < numIter; i++) defectiveSample(totalSize, sampleSize, r1); long tDef = System.nanoTime() - t0; t0 = System.nanoTime(); Random r2 = new Random(99); for (int i = 0; i < numIter; i++) fixedSample(totalSize, sampleSize, r2); long tFix = System.nanoTime() - t0; double ratio = (double) tDef / tFix; System.out.printf(" Perf RANSAC iter=%d S=%d N=%d: defective=%.1fms fixed=%.1fms ratio=%.1fx%n", numIter, sampleSize, totalSize, tDef / 1e6, tFix / 1e6, ratio); assertTrue("perf-s100-speedup", ratio > 2.0); } System.out.println(pass + "/" + total + " PASS"); assert pass == total : pass + "/" + total + " passed"; } }