permute.odin 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package slice
  2. import "base:runtime"
  3. // An in-place permutation iterator.
  4. Permutation_Iterator :: struct($T: typeid) {
  5. index: int,
  6. slice: []T,
  7. counters: []int,
  8. }
  9. /*
  10. Make an iterator to permute a slice in-place.
  11. *Allocates Using Provided Allocator*
  12. This procedure allocates some state to assist in permutation and does not make
  13. a copy of the underlying slice. If you want to permute a slice without altering
  14. the underlying data, use `clone` to create a copy, then permute that instead.
  15. Inputs:
  16. - slice: The slice to permute.
  17. - allocator: (default is context.allocator)
  18. Returns:
  19. - iter: The iterator, to be passed to `permute`.
  20. - error: An `Allocator_Error`, if allocation failed.
  21. */
  22. make_permutation_iterator :: proc(
  23. slice: []$T,
  24. allocator := context.allocator,
  25. ) -> (
  26. iter: Permutation_Iterator(T),
  27. error: runtime.Allocator_Error,
  28. ) #optional_allocator_error {
  29. iter.slice = slice
  30. iter.counters = make([]int, len(iter.slice), allocator) or_return
  31. return
  32. }
  33. /*
  34. Free the state allocated by `make_permutation_iterator`.
  35. Inputs:
  36. - iter: The iterator created by `make_permutation_iterator`.
  37. - allocator: The allocator used to create the iterator. (default is context.allocator)
  38. */
  39. destroy_permutation_iterator :: proc(
  40. iter: Permutation_Iterator($T),
  41. allocator := context.allocator,
  42. ) {
  43. delete(iter.counters, allocator = allocator)
  44. }
  45. /*
  46. Permute a slice in-place.
  47. Note that the first iteration will always be the original, unpermuted slice.
  48. Inputs:
  49. - iter: The iterator created by `make_permutation_iterator`.
  50. Returns:
  51. - ok: True if the permutation succeeded, false if the iteration is complete.
  52. */
  53. permute :: proc(iter: ^Permutation_Iterator($T)) -> (ok: bool) {
  54. // This is an iterative, resumable implementation of Heap's algorithm.
  55. //
  56. // The original algorithm was described by B. R. Heap as "Permutations by
  57. // interchanges" in The Computer Journal, 1963.
  58. //
  59. // This implementation is based on the nonrecursive version described by
  60. // Robert Sedgewick in "Permutation Generation Methods" which was published
  61. // in ACM Computing Surveys in 1977.
  62. i := iter.index
  63. if i == 0 {
  64. iter.index = 1
  65. return true
  66. }
  67. n := len(iter.counters)
  68. #no_bounds_check for i < n {
  69. if iter.counters[i] < i {
  70. if i & 1 == 0 {
  71. iter.slice[0], iter.slice[i] = iter.slice[i], iter.slice[0]
  72. } else {
  73. iter.slice[iter.counters[i]], iter.slice[i] = iter.slice[i], iter.slice[iter.counters[i]]
  74. }
  75. iter.counters[i] += 1
  76. i = 1
  77. break
  78. } else {
  79. iter.counters[i] = 0
  80. i += 1
  81. }
  82. }
  83. if i == n {
  84. return false
  85. }
  86. iter.index = i
  87. return true
  88. }