|
| 1 | +/- |
| 2 | +Copyright (c) 2014 Parikshit Khanna. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro |
| 5 | +
|
| 6 | +! This file was ported from Lean 3 source module data.list.permutation |
| 7 | +! leanprover-community/mathlib commit dd71334db81d0bd444af1ee339a29298bef40734 |
| 8 | +! Please do not edit these lines, except to modify the commit id |
| 9 | +! if you have ported upstream changes. |
| 10 | +-/ |
| 11 | +import Mathlib.Data.List.Join |
| 12 | + |
| 13 | +/-! |
| 14 | +# Permutations of a list |
| 15 | +
|
| 16 | +In this file we prove properties about `List.Permutations`, a list of all permutations of a list. It |
| 17 | +is defined in `Data.List.Defs`. |
| 18 | +
|
| 19 | +## Order of the permutations |
| 20 | +
|
| 21 | +Designed for performance, the order in which the permutations appear in `List.Permutations` is |
| 22 | +rather intricate and not very amenable to induction. That's why we also provide `List.Permutations'` |
| 23 | +as a less efficient but more straightforward way of listing permutations. |
| 24 | +
|
| 25 | +### `List.Permutations` |
| 26 | +
|
| 27 | +TODO. In the meantime, you can try decrypting the docstrings. |
| 28 | +
|
| 29 | +### `List.Permutations'` |
| 30 | +
|
| 31 | +The list of partitions is built by recursion. The permutations of `[]` are `[[]]`. Then, the |
| 32 | +permutations of `a :: l` are obtained by taking all permutations of `l` in order and adding `a` in |
| 33 | +all positions. Hence, to build `[0, 1, 2, 3].permutations'`, it does |
| 34 | +* `[[]]` |
| 35 | +* `[[3]]` |
| 36 | +* `[[2, 3], [3, 2]]]` |
| 37 | +* `[[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]` |
| 38 | +* `[[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3], [1, 2, 3, 0],` |
| 39 | + `[0, 2, 1, 3], [2, 0, 1, 3], [2, 1, 0, 3], [2, 1, 3, 0],` |
| 40 | + `[0, 2, 3, 1], [2, 0, 3, 1], [2, 3, 0, 1], [2, 3, 1, 0],` |
| 41 | + `[0, 1, 3, 2], [1, 0, 3, 2], [1, 3, 0, 2], [1, 3, 2, 0],` |
| 42 | + `[0, 3, 1, 2], [3, 0, 1, 2], [3, 1, 0, 2], [3, 1, 2, 0],` |
| 43 | + `[0, 3, 2, 1], [3, 0, 2, 1], [3, 2, 0, 1], [3, 2, 1, 0]]` |
| 44 | +
|
| 45 | +## TODO |
| 46 | +
|
| 47 | +Show that `l.Nodup → l.permutations.Nodup`. See `Data.Fintype.List`. |
| 48 | +-/ |
| 49 | + |
| 50 | + |
| 51 | +open Nat |
| 52 | + |
| 53 | +variable {α β : Type _} |
| 54 | + |
| 55 | +namespace List |
| 56 | + |
| 57 | +theorem permutationsAux2_fst (t : α) (ts : List α) (r : List β) : |
| 58 | + ∀ (ys : List α) (f : List α → β), (permutationsAux2 t ts r ys f).1 = ys ++ ts |
| 59 | + | [], f => rfl |
| 60 | + | y :: ys, f => by simp [permutationsAux2, permutationsAux2_fst t _ _ ys] |
| 61 | +#align list.permutations_aux2_fst List.permutationsAux2_fst |
| 62 | + |
| 63 | +@[simp] |
| 64 | +theorem permutations_aux2_snd_nil (t : α) (ts : List α) (r : List β) (f : List α → β) : |
| 65 | + (permutationsAux2 t ts r [] f).2 = r := |
| 66 | + rfl |
| 67 | +#align list.permutations_aux2_snd_nil List.permutations_aux2_snd_nil |
| 68 | + |
| 69 | +@[simp] |
| 70 | +theorem permutationsAux2_snd_cons (t : α) (ts : List α) (r : List β) (y : α) (ys : List α) |
| 71 | + (f : List α → β) : |
| 72 | + (permutationsAux2 t ts r (y :: ys) f).2 = |
| 73 | + f (t :: y :: ys ++ ts) :: (permutationsAux2 t ts r ys fun x : List α => f (y :: x)).2 := |
| 74 | + by simp [permutationsAux2, permutationsAux2_fst t _ _ ys] |
| 75 | +#align list.permutations_aux2_snd_cons List.permutationsAux2_snd_cons |
| 76 | + |
| 77 | +/-- The `r` argument to `permutations_aux2` is the same as appending. -/ |
| 78 | +theorem permutationsAux2_append (t : α) (ts : List α) (r : List β) (ys : List α) (f : List α → β) : |
| 79 | + (permutationsAux2 t ts nil ys f).2 ++ r = (permutationsAux2 t ts r ys f).2 := by |
| 80 | + induction ys generalizing f <;> simp [*] |
| 81 | +#align list.permutations_aux2_append List.permutationsAux2_append |
| 82 | + |
| 83 | +/-- The `ts` argument to `permutations_aux2` can be folded into the `f` argument. -/ |
| 84 | +theorem permutationsAux2_comp_append {t : α} {ts ys : List α} {r : List β} (f : List α → β) : |
| 85 | + ((permutationsAux2 t [] r ys) fun x => f (x ++ ts)).2 = (permutationsAux2 t ts r ys f).2 := by |
| 86 | + induction' ys with ys_hd _ ys_ih generalizing f |
| 87 | + · simp |
| 88 | + · simp [ys_ih fun xs => f (ys_hd :: xs)] |
| 89 | +#align list.permutations_aux2_comp_append List.permutationsAux2_comp_append |
| 90 | + |
| 91 | +theorem map_permutationsAux2' {α β α' β'} (g : α → α') (g' : β → β') (t : α) (ts ys : List α) |
| 92 | + (r : List β) (f : List α → β) (f' : List α' → β') (H : ∀ a, g' (f a) = f' (map g a)) : |
| 93 | + map g' (permutationsAux2 t ts r ys f).2 = |
| 94 | + (permutationsAux2 (g t) (map g ts) (map g' r) (map g ys) f').2 := |
| 95 | + by |
| 96 | + induction' ys with ys_hd _ ys_ih generalizing f f' |
| 97 | + . simp |
| 98 | + . simp only [map, permutationsAux2_snd_cons, cons_append, cons.injEq] |
| 99 | + rw [ys_ih, permutationsAux2_fst] |
| 100 | + refine' ⟨_, rfl⟩ |
| 101 | + . simp only [← map_cons, ← map_append]; apply H |
| 102 | + . intro a; apply H |
| 103 | +#align list.map_permutations_aux2' List.map_permutationsAux2' |
| 104 | + |
| 105 | +/-- The `f` argument to `permutations_aux2` when `r = []` can be eliminated. -/ |
| 106 | +theorem map_permutationsAux2 (t : α) (ts : List α) (ys : List α) (f : List α → β) : |
| 107 | + (permutationsAux2 t ts [] ys id).2.map f = (permutationsAux2 t ts [] ys f).2 := |
| 108 | + by |
| 109 | + rw [map_permutationsAux2' id, map_id, map_id]; rfl |
| 110 | + simp |
| 111 | +#align list.map_permutations_aux2 List.map_permutationsAux2 |
| 112 | + |
| 113 | +/-- An expository lemma to show how all of `ts`, `r`, and `f` can be eliminated from |
| 114 | +`permutations_aux2`. |
| 115 | +
|
| 116 | +`(permutations_aux2 t [] [] ys id).2`, which appears on the RHS, is a list whose elements are |
| 117 | +produced by inserting `t` into every non-terminal position of `ys` in order. As an example: |
| 118 | +```lean |
| 119 | +#eval permutations_aux2 1 [] [] [2, 3, 4] id |
| 120 | +-- [[1, 2, 3, 4], [2, 1, 3, 4], [2, 3, 1, 4]] |
| 121 | +``` |
| 122 | +-/ |
| 123 | +theorem permutationsAux2_snd_eq (t : α) (ts : List α) (r : List β) (ys : List α) (f : List α → β) : |
| 124 | + (permutationsAux2 t ts r ys f).2 = |
| 125 | + ((permutationsAux2 t [] [] ys id).2.map fun x => f (x ++ ts)) ++ r := |
| 126 | + by rw [← permutationsAux2_append, map_permutationsAux2, permutationsAux2_comp_append] |
| 127 | +#align list.permutations_aux2_snd_eq List.permutationsAux2_snd_eq |
| 128 | + |
| 129 | +theorem map_map_permutationsAux2 {α α'} (g : α → α') (t : α) (ts ys : List α) : |
| 130 | + map (map g) (permutationsAux2 t ts [] ys id).2 = |
| 131 | + (permutationsAux2 (g t) (map g ts) [] (map g ys) id).2 := |
| 132 | + map_permutationsAux2' _ _ _ _ _ _ _ _ fun _ => rfl |
| 133 | +#align list.map_map_permutations_aux2 List.map_map_permutationsAux2 |
| 134 | + |
| 135 | +theorem map_map_permutations'Aux (f : α → β) (t : α) (ts : List α) : |
| 136 | + map (map f) (permutations'Aux t ts) = permutations'Aux (f t) (map f ts) := by |
| 137 | + induction' ts with a ts ih <;> [rfl, |
| 138 | + · simp [← ih] |
| 139 | + rfl] |
| 140 | +#align list.map_map_permutations'_aux List.map_map_permutations'Aux |
| 141 | + |
| 142 | +theorem permutations'Aux_eq_permutationsAux2 (t : α) (ts : List α) : |
| 143 | + permutations'Aux t ts = (permutationsAux2 t [] [ts ++ [t]] ts id).2 := |
| 144 | + by |
| 145 | + induction' ts with a ts ih; · rfl |
| 146 | + simp [permutations'Aux, permutationsAux2_snd_cons, ih] |
| 147 | + simp (config := { singlePass := true }) only [← permutationsAux2_append] |
| 148 | + simp [map_permutationsAux2] |
| 149 | +#align list.permutations'_aux_eq_permutations_aux2 List.permutations'Aux_eq_permutationsAux2 |
| 150 | + |
| 151 | +theorem mem_permutationsAux2 {t : α} {ts : List α} {ys : List α} {l l' : List α} : |
| 152 | + l' ∈ (permutationsAux2 t ts [] ys (l ++ .)).2 ↔ |
| 153 | + ∃ l₁ l₂, l₂ ≠ [] ∧ ys = l₁ ++ l₂ ∧ l' = l ++ l₁ ++ t :: l₂ ++ ts := |
| 154 | + by |
| 155 | + induction' ys with y ys ih generalizing l |
| 156 | + · simp (config := { contextual := true }) |
| 157 | + rw [permutationsAux2_snd_cons, |
| 158 | + show (fun x : List α => l ++ y :: x) = (l ++ [y] ++ .) by funext _; simp, mem_cons, ih] |
| 159 | + constructor |
| 160 | + · rintro (rfl | ⟨l₁, l₂, l0, rfl, rfl⟩) |
| 161 | + · exact ⟨[], y :: ys, by simp⟩ |
| 162 | + · exact ⟨y :: l₁, l₂, l0, by simp⟩ |
| 163 | + · rintro ⟨_ | ⟨y', l₁⟩, l₂, l0, ye, rfl⟩ |
| 164 | + · simp [ye] |
| 165 | + · simp only [cons_append] at ye |
| 166 | + rcases ye with ⟨rfl, rfl⟩ |
| 167 | + exact Or.inr ⟨l₁, l₂, l0, by simp⟩ |
| 168 | +#align list.mem_permutations_aux2 List.mem_permutationsAux2 |
| 169 | + |
| 170 | +theorem mem_permutationsAux2' {t : α} {ts : List α} {ys : List α} {l : List α} : |
| 171 | + l ∈ (permutationsAux2 t ts [] ys id).2 ↔ |
| 172 | + ∃ l₁ l₂, l₂ ≠ [] ∧ ys = l₁ ++ l₂ ∧ l = l₁ ++ t :: l₂ ++ ts := |
| 173 | + by rw [show @id (List α) = ([] ++ .) by funext _; rfl]; apply mem_permutationsAux2 |
| 174 | +#align list.mem_permutations_aux2' List.mem_permutationsAux2' |
| 175 | + |
| 176 | +theorem length_permutationsAux2 (t : α) (ts : List α) (ys : List α) (f : List α → β) : |
| 177 | + length (permutationsAux2 t ts [] ys f).2 = length ys := by |
| 178 | + induction ys generalizing f <;> simp [*] |
| 179 | +#align list.length_permutations_aux2 List.length_permutationsAux2 |
| 180 | + |
| 181 | +theorem foldr_permutationsAux2 (t : α) (ts : List α) (r L : List (List α)) : |
| 182 | + foldr (fun y r => (permutationsAux2 t ts r y id).2) r L = |
| 183 | + (L.bind fun y => (permutationsAux2 t ts [] y id).2) ++ r := |
| 184 | + by |
| 185 | + induction' L with l L ih <;> [rfl, |
| 186 | + · simp [ih] |
| 187 | + rw [← permutationsAux2_append]] |
| 188 | +#align list.foldr_permutations_aux2 List.foldr_permutationsAux2 |
| 189 | + |
| 190 | +theorem mem_foldr_permutationsAux2 {t : α} {ts : List α} {r L : List (List α)} {l' : List α} : |
| 191 | + l' ∈ foldr (fun y r => (permutationsAux2 t ts r y id).2) r L ↔ |
| 192 | + l' ∈ r ∨ ∃ l₁ l₂, l₁ ++ l₂ ∈ L ∧ l₂ ≠ [] ∧ l' = l₁ ++ t :: l₂ ++ ts := by |
| 193 | + have : |
| 194 | + (∃ a : List α, |
| 195 | + a ∈ L ∧ ∃ l₁ l₂ : List α, ¬l₂ = nil ∧ a = l₁ ++ l₂ ∧ l' = l₁ ++ t :: (l₂ ++ ts)) ↔ |
| 196 | + ∃ l₁ l₂ : List α, ¬l₂ = nil ∧ l₁ ++ l₂ ∈ L ∧ l' = l₁ ++ t :: (l₂ ++ ts) := |
| 197 | + ⟨fun ⟨_, aL, l₁, l₂, l0, e, h⟩ => ⟨l₁, l₂, l0, e ▸ aL, h⟩, fun ⟨l₁, l₂, l0, aL, h⟩ => |
| 198 | + ⟨_, aL, l₁, l₂, l0, rfl, h⟩⟩ |
| 199 | + rw [foldr_permutationsAux2] |
| 200 | + simp only [mem_permutationsAux2', ← this, or_comm, and_left_comm, mem_append, mem_bind, |
| 201 | + append_assoc, cons_append, exists_prop] |
| 202 | +#align list.mem_foldr_permutations_aux2 List.mem_foldr_permutationsAux2 |
| 203 | + |
| 204 | +theorem length_foldr_permutationsAux2 (t : α) (ts : List α) (r L : List (List α)) : |
| 205 | + length (foldr (fun y r => (permutationsAux2 t ts r y id).2) r L) = |
| 206 | + sum (map length L) + length r := |
| 207 | + by simp [foldr_permutationsAux2, (· ∘ ·), length_permutationsAux2] |
| 208 | +#align list.length_foldr_permutations_aux2 List.length_foldr_permutationsAux2 |
| 209 | + |
| 210 | +theorem length_foldr_permutationsAux2' (t : α) (ts : List α) (r L : List (List α)) (n) |
| 211 | + (H : ∀ l ∈ L, length l = n) : |
| 212 | + length (foldr (fun y r => (permutationsAux2 t ts r y id).2) r L) = n * length L + length r := by |
| 213 | + rw [length_foldr_permutationsAux2, (_ : List.sum (map length L) = n * length L)] |
| 214 | + induction' L with l L ih; · simp |
| 215 | + have sum_map : sum (map length L) = n * length L := ih fun l m => H l (mem_cons_of_mem _ m) |
| 216 | + have length_l : length l = n := H _ (mem_cons_self _ _) |
| 217 | + simp [sum_map, length_l, mul_add, add_comm, mul_succ] |
| 218 | +#align list.length_foldr_permutations_aux2' List.length_foldr_permutationsAux2' |
| 219 | + |
| 220 | +@[simp] |
| 221 | +theorem permutationsAux_nil (is : List α) : permutationsAux [] is = [] := by |
| 222 | + rw [permutationsAux, permutationsAux.rec] |
| 223 | +#align list.permutations_aux_nil List.permutationsAux_nil |
| 224 | + |
| 225 | +@[simp] |
| 226 | +theorem permutationsAux_cons (t : α) (ts is : List α) : |
| 227 | + permutationsAux (t :: ts) is = |
| 228 | + foldr (fun y r => (permutationsAux2 t ts r y id).2) (permutationsAux ts (t :: is)) |
| 229 | + (permutations is) := |
| 230 | + by rw [permutationsAux, permutationsAux.rec]; rfl |
| 231 | +#align list.permutations_aux_cons List.permutationsAux_cons |
| 232 | + |
| 233 | +@[simp] |
| 234 | +theorem permutations_nil : permutations ([] : List α) = [[]] := by |
| 235 | + rw [permutations, permutationsAux_nil] |
| 236 | +#align list.permutations_nil List.permutations_nil |
| 237 | + |
| 238 | +theorem map_permutationsAux (f : α → β) : |
| 239 | + ∀ ts is : List α, map (map f) (permutationsAux ts is) = permutationsAux (map f ts) (map f is) := |
| 240 | + by |
| 241 | + refine' permutationsAux.rec (by simp) _ |
| 242 | + introv IH1 IH2; rw [map] at IH2 |
| 243 | + simp only [foldr_permutationsAux2, map_append, map, map_map_permutationsAux2, permutations, |
| 244 | + bind_map, IH1, append_assoc, permutationsAux_cons, cons_bind, ← IH2, map_bind] |
| 245 | +#align list.map_permutations_aux List.map_permutationsAux |
| 246 | + |
| 247 | +theorem map_permutations (f : α → β) (ts : List α) : |
| 248 | + map (map f) (permutations ts) = permutations (map f ts) := by |
| 249 | + rw [permutations, permutations, map, map_permutationsAux, map] |
| 250 | +#align list.map_permutations List.map_permutations |
| 251 | + |
| 252 | +theorem map_permutations' (f : α → β) (ts : List α) : |
| 253 | + map (map f) (permutations' ts) = permutations' (map f ts) := by |
| 254 | + induction' ts with t ts ih <;> [rfl, simp [← ih, map_bind, ← map_map_permutations'Aux, bind_map]] |
| 255 | +#align list.map_permutations' List.map_permutations' |
| 256 | + |
| 257 | +theorem permutationsAux_append (is is' ts : List α) : |
| 258 | + permutationsAux (is ++ ts) is' = |
| 259 | + (permutationsAux is is').map (· ++ ts) ++ permutationsAux ts (is.reverse ++ is') := by |
| 260 | + induction' is with t is ih generalizing is'; · simp |
| 261 | + simp only [foldr_permutationsAux2, ih, bind_map, cons_append, permutationsAux_cons, map_append, |
| 262 | + reverse_cons, append_assoc, singleton_append] |
| 263 | + congr 2 |
| 264 | + funext _ |
| 265 | + rw [map_permutationsAux2] |
| 266 | + simp (config := { singlePass := true }) only [← permutationsAux2_comp_append] |
| 267 | + simp only [id, append_assoc] |
| 268 | +#align list.permutations_aux_append List.permutationsAux_append |
| 269 | + |
| 270 | +theorem permutations_append (is ts : List α) : |
| 271 | + permutations (is ++ ts) = (permutations is).map (· ++ ts) ++ permutationsAux ts is.reverse := by |
| 272 | + simp [permutations, permutationsAux_append] |
| 273 | +#align list.permutations_append List.permutations_append |
| 274 | + |
| 275 | +end List |
0 commit comments