SortUtils.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /** @module SortUtils */
  2. const POWER = 3;
  3. const BIT_MAX = 32;
  4. const BIN_BITS = 1 << POWER;
  5. const BIN_SIZE = 1 << BIN_BITS;
  6. const BIN_MAX = BIN_SIZE - 1;
  7. const ITERATIONS = BIT_MAX / BIN_BITS;
  8. const bins = new Array( ITERATIONS );
  9. const bins_buffer = new ArrayBuffer( ( ITERATIONS + 1 ) * BIN_SIZE * 4 );
  10. let c = 0;
  11. for ( let i = 0; i < ( ITERATIONS + 1 ); i ++ ) {
  12. bins[ i ] = new Uint32Array( bins_buffer, c, BIN_SIZE );
  13. c += BIN_SIZE * 4;
  14. }
  15. const defaultGet = ( el ) => el;
  16. /**
  17. * Hybrid radix sort from.
  18. *
  19. * - {@link https://gist.github.com/sciecode/93ed864dd77c5c8803c6a86698d68dab}
  20. * - {@link https://github.com/mrdoob/three.js/pull/27202#issuecomment-1817640271}
  21. *
  22. * Expects unsigned 32b integer values.
  23. *
  24. * @function
  25. * @param {Array<Object>} arr - The array to sort.
  26. * @param {Object} opt - The options
  27. */
  28. export const radixSort = ( arr, opt ) => {
  29. const len = arr.length;
  30. const options = opt || {};
  31. const aux = options.aux || new arr.constructor( len );
  32. const get = options.get || defaultGet;
  33. const data = [ arr, aux ];
  34. let compare, accumulate, recurse;
  35. if ( options.reversed ) {
  36. compare = ( a, b ) => a < b;
  37. accumulate = ( bin ) => {
  38. for ( let j = BIN_SIZE - 2; j >= 0; j -- )
  39. bin[ j ] += bin[ j + 1 ];
  40. };
  41. recurse = ( cache, depth, start ) => {
  42. let prev = 0;
  43. for ( let j = BIN_MAX; j >= 0; j -- ) {
  44. const cur = cache[ j ], diff = cur - prev;
  45. if ( diff != 0 ) {
  46. if ( diff > 32 )
  47. radixSortBlock( depth + 1, start + prev, diff );
  48. else
  49. insertionSortBlock( depth + 1, start + prev, diff );
  50. prev = cur;
  51. }
  52. }
  53. };
  54. } else {
  55. compare = ( a, b ) => a > b;
  56. accumulate = ( bin ) => {
  57. for ( let j = 1; j < BIN_SIZE; j ++ )
  58. bin[ j ] += bin[ j - 1 ];
  59. };
  60. recurse = ( cache, depth, start ) => {
  61. let prev = 0;
  62. for ( let j = 0; j < BIN_SIZE; j ++ ) {
  63. const cur = cache[ j ], diff = cur - prev;
  64. if ( diff != 0 ) {
  65. if ( diff > 32 )
  66. radixSortBlock( depth + 1, start + prev, diff );
  67. else
  68. insertionSortBlock( depth + 1, start + prev, diff );
  69. prev = cur;
  70. }
  71. }
  72. };
  73. }
  74. const insertionSortBlock = ( depth, start, len ) => {
  75. const a = data[ depth & 1 ];
  76. const b = data[ ( depth + 1 ) & 1 ];
  77. for ( let j = start + 1; j < start + len; j ++ ) {
  78. const p = a[ j ], t = get( p ) >>> 0;
  79. let i = j;
  80. while ( i > start ) {
  81. if ( compare( get( a[ i - 1 ] ) >>> 0, t ) )
  82. a[ i ] = a[ -- i ];
  83. else
  84. break;
  85. }
  86. a[ i ] = p;
  87. }
  88. if ( ( depth & 1 ) == 1 ) {
  89. for ( let i = start; i < start + len; i ++ )
  90. b[ i ] = a[ i ];
  91. }
  92. };
  93. const radixSortBlock = ( depth, start, len ) => {
  94. const a = data[ depth & 1 ];
  95. const b = data[ ( depth + 1 ) & 1 ];
  96. const shift = ( 3 - depth ) << POWER;
  97. const end = start + len;
  98. const cache = bins[ depth ];
  99. const bin = bins[ depth + 1 ];
  100. bin.fill( 0 );
  101. for ( let j = start; j < end; j ++ )
  102. bin[ ( get( a[ j ] ) >>> shift ) & BIN_MAX ] ++;
  103. accumulate( bin );
  104. cache.set( bin );
  105. for ( let j = end - 1; j >= start; j -- )
  106. b[ start + -- bin[ ( get( a[ j ] ) >>> shift ) & BIN_MAX ] ] = a[ j ];
  107. if ( depth == ITERATIONS - 1 ) return;
  108. recurse( cache, depth, start );
  109. };
  110. radixSortBlock( 0, 0, len );
  111. };
粤ICP备19079148号