片方向の連結リスト (singly-linked list) です。詳しい説明は拙作のページ Algorithms with Python 「連結リスト」をお読みください。なお、今回は C# のお勉強ということでインデクサーを実装しましたが、要素数を N とすると、要素のアクセスは O(N) に比例する時間がかかります。配列のようにランダムアクセスする用途には向きません。ご注意くださいませ。
リスト : 連結リスト (lintlist.csx, 非ジェネリック版) using System.Collections; class LinkList : IEnumerable { class Cell { public object Car { get; set; } public Cell Cdr { get; set; } public Cell(object item, Cell next) { Car = item; Cdr = next; } } // ヘッダーセル Cell header; // 空リストの生成 public LinkList() { header = new Cell(null, null); } // n 番目のセルを求める private Cell NthCell(int n) { Cell cp = header; for (int i = -1; i < n; i++) { if (cp == null) throw new IndexOutOfRangeException(); cp = cp.Cdr; } return cp; } // インデクサー public object this[int n] { get { return NthCell(n).Car; } set { NthCell(n).Car = value; } } // 挿入 public void Insert(object item, int n) { Cell cp = NthCell(n - 1); cp.Cdr = new Cell(item, cp.Cdr); } // 削除 public void Delete(int n) { Cell cp = NthCell(n - 1); if (cp.Cdr == null) throw new IndexOutOfRangeException(); cp.Cdr = cp.Cdr.Cdr; } // イテレータ public IEnumerator GetEnumerator() { var cp = header.Cdr; while (cp != null) { yield return cp.Car; cp = cp.Cdr; } } } void PrintList(LinkList a) { foreach(object x in a) Console.Write("{0} ", x); Console.WriteLine(""); } void test() { var a = new LinkList(); for (int i = 0; i < 10; i++) { a.Insert(i, i); } PrintList(a); for (int i = 0; i < 10; i++) { a[i] = (int)a[i] * 10; Console.Write("{0} ", a[i]); } Console.WriteLine(""); a.Delete(0); PrintList(a); a.Delete(8); PrintList(a); a.Delete(4); PrintList(a); } // テスト test();
$ dotnet script linklist.csx 0 1 2 3 4 5 6 7 8 9 0 10 20 30 40 50 60 70 80 90 10 20 30 40 50 60 70 80 90 10 20 30 40 50 60 70 80 10 20 30 40 60 70 80
リスト : 連結リスト (ジェネリック版, linklist1.csx) using System.Collections; using System.Collections.Generic; class LinkList<T> : IEnumerable<T> where T : IComparable<T> { class Cell { public T Car { get; set; } public Cell Cdr { get; set; } public Cell(T item, Cell next) { Car = item; Cdr = next; } } // ヘッダーセル Cell header; // 空リストの生成 public LinkList() { header = new Cell(default(T), null); } // n 番目のセルを求める private Cell NthCell(int n) { Cell xs = header; for (int i = -1; i < n; i++) { if (xs == null) throw new IndexOutOfRangeException(); xs = xs.Cdr; } return xs; } // インデクサー public T this[int n] { get { return NthCell(n).Car; } set { NthCell(n).Car = value; } } // 挿入 public void Insert(T item, int n) { Cell xs = NthCell(n - 1); xs.Cdr = new Cell(item, xs.Cdr); } // 削除 public void Delete(int n) { Cell xs = NthCell(n - 1); if (xs.Cdr == null) throw new IndexOutOfRangeException(); xs.Cdr = xs.Cdr.Cdr; } // イテレータ public IEnumerator<T> GetEnumerator() { var xs = header.Cdr; while (xs != null) { yield return xs.Car; xs = xs.Cdr; } } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } // 探索 public bool Contains(T item) { foreach(T x in this) { if (item.CompareTo(x) == 0) return true; } return false; } public int IndexOf(T item) { int i = 0; var xs = header.Cdr; while (xs != null) { if (item.CompareTo(xs.Car) == 0) return i; xs = xs.Cdr; i++; } return -1; } public int FindIndex(Func<T, bool> pred) { int i = 0; var xs = header.Cdr; while (xs != null) { if (pred(xs.Car)) return i; xs = xs.Cdr; i++; } return -1; } } void PrintList<T>(LinkList<T> a) where T : IComparable<T> { foreach(T x in a) Console.Write("{0} ", x); Console.WriteLine(""); } void test() { var a = new LinkList<int>(); for (int i = 0; i < 10; i++) { a.Insert(i, i); } PrintList(a); for (int i = -1; i < 11; i++) { Console.WriteLine("{0}, {1}", i, a.Contains(i)); Console.WriteLine("{0}, {1}", i, a.IndexOf(i)); Console.WriteLine("{0}, {1}", i, a.FindIndex(x => x == i)); } for (int i = 0; i < 10; i++) { a[i] *= 10; Console.Write("{0} ", a[i]); } Console.WriteLine(""); a.Delete(0); PrintList(a); a.Delete(8); PrintList(a); a.Delete(4); PrintList(a); } // テスト test();
$ dotnet script linklist1.csx 0 1 2 3 4 5 6 7 8 9 -1, False -1, -1 -1, -1 0, True 0, 0 0, 0 1, True 1, 1 1, 1 2, True 2, 2 2, 2 3, True 3, 3 3, 3 4, True 4, 4 4, 4 5, True 5, 5 5, 5 6, True 6, 6 6, 6 7, True 7, 7 7, 7 8, True 8, 8 8, 8 9, True 9, 9 9, 9 10, False 10, -1 10, -1 0 10 20 30 40 50 60 70 80 90 10 20 30 40 50 60 70 80 90 10 20 30 40 50 60 70 80 10 20 30 40 60 70 80
リスト : immutable な連結リスト (ジェネリック版, linklist2.csx) using System.Collections; using System.Collections.Generic; // immutable な連結リスト public class Cell<T> : IEnumerable<T> where T : IComparable { public T Car { get; } public Cell<T> Cdr { get; } public Cell(T item, Cell<T> next) { Car = item; Cdr = next; } // 終端 public static readonly Cell<T> nil = new Cell<T>(default(T), null); public bool Null() { return this == nil; } // イテレータ public IEnumerator<T> GetEnumerator() { var xs = this; while (!xs.Null()) { yield return xs.Car; xs = xs.Cdr; } } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } // リストの生成 public static Cell<T> List(params T[] xs) { var ys = nil; for (int i = xs.Length - 1; i >= 0; i--) { ys = new Cell<T>(xs[i], ys); } return ys; } // リストの連結 public Cell<T> Append(Cell<T> xs) { if (this.Null()) return xs; else return new Cell<T>(Car, Cdr.Append(xs)); } // リストの反転 public Cell<T> Reverse() { var ys = nil; foreach(T x in this) { ys = new Cell<T>(x, ys); } return ys; } // リストの長さ public int Length() { int c = 0; foreach(T x in this) { c++; } return c; } // 参照 public T Nth(int n) { foreach(T x in this) { if (n-- == 0) return x; } throw new IndexOutOfRangeException(); } // 探索 public Cell<T> Member(T item) { var xs = this; while (!xs.Null()) { if (item.CompareTo(xs.Car) == 0) break; xs = xs.Cdr; } return xs; } public bool Contains(T item) { foreach(T x in this) { if (item.CompareTo(x) == 0) return true; } return false; } public int IndexOf(T item) { int i = 0; foreach(T x in this) { if (item.CompareTo(x) == 0) return i; i++; } return -1; } public int FindIndex(Func<T, bool> pred) { int i = 0; foreach(T x in this) { if (pred(x)) return i; i++; } return -1; } // マッピング public Cell<U> Map<U>(Func<T, U> func) where U : IComparable { if (this.Null()) return Cell<U>.nil; else return new Cell<U>(func(Car), Cdr.Map(func)); } // フィルター public Cell<T> Filter(Func<T, bool> pred) { if (this.Null()) return nil; else if (pred(Car)) return new Cell<T>(Car, Cdr.Filter(pred)); else return Cdr.Filter(pred); } // 畳み込み public U FoldLeft<U>(Func<U, T, U> func, U a) { foreach(T x in this) { a = func(a, x); } return a; } public U FoldRight<U>(Func<T, U, U> func, U a) { if (this.Null()) return a; else return func(Car, Cdr.FoldRight(func, a)); } // 巡回 public void Each(Action<T> func) { foreach(T x in this) { func(x); } } // 表示 public void Print() { var xs = this; Console.Write("("); while (!xs.Null()) { Console.Write("{0}", xs.Car); xs = xs.Cdr; if (!xs.Null()) Console.Write(" "); } Console.WriteLine(")"); } } void test() { var a = Cell<int>.List(1, 2, 3, 4, 5); a.Print(); Console.WriteLine("{0}", a.Length()); a.Reverse().Print(); var b = Cell<int>.List(6, 7, 8, 9, 10); var c = a.Append(b); c.Print(); for (int i = 0; i < c.Length(); i++) Console.Write("{0} ", c.Nth(i)); Console.WriteLine(""); for (int i = 0; i <= 11; i++) { c.Member(i).Print(); Console.WriteLine("{0}", c.Contains(i)); Console.WriteLine("{0}", c.IndexOf(i)); Console.WriteLine("{0}", c.FindIndex(n => n == i)); } c.Map(n => n * 10).Print(); c.Filter(n => n % 2 == 0).Print(); Console.WriteLine("{0}", c.FoldLeft((sum, n) => sum + n, 0)); Console.WriteLine("{0}", c.FoldRight((n, sum) => sum + n, 0)); c.Each(n => Console.Write("{0} ", n)); Console.WriteLine(""); foreach(int n in c) { Console.Write("{0} ", n); } Console.WriteLine(""); } // テスト test();
$ dotnet script linklist2.csx (1 2 3 4 5) 5 (5 4 3 2 1) (1 2 3 4 5 6 7 8 9 10) 1 2 3 4 5 6 7 8 9 10 () False -1 -1 (1 2 3 4 5 6 7 8 9 10) True 0 0 (2 3 4 5 6 7 8 9 10) True 1 1 (3 4 5 6 7 8 9 10) True 2 2 (4 5 6 7 8 9 10) True 3 3 (5 6 7 8 9 10) True 4 4 (6 7 8 9 10) True 5 5 (7 8 9 10) True 6 6 (8 9 10) True 7 7 (9 10) True 8 8 (10) True 9 9 () False -1 -1 (10 20 30 40 50 60 70 80 90 100) (2 4 6 8 10) 55 55 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
リスト : Lisp ライクな連結リスト (immutable, linklist3.csx) using System.Collections; using static Cell; class Cell : IEnumerable { public object Car { get; } public Cell Cdr { get; } // ドットリストはサポートしない public Cell(object item, Cell next) { Car = item; Cdr = next; } // 終端 public static readonly Cell nil = new Cell(null, null); // 述語 public static bool Null(object xs) { return xs == nil; } public static bool Consp(object xs) { return xs is Cell; } public static bool Listp(object xs) { return Null(xs) || Consp(xs); } public static bool Eql(object x, object y) { return ((IComparable)x).CompareTo((IComparable)y) == 0; } // イテレータ public IEnumerator GetEnumerator() { var xs = this; while (!Null(xs)) { yield return xs.Car; xs = xs.Cdr; } } // リストの生成 public static Cell List(params object[] xs) { var ys = nil; for (int i = xs.Length - 1; i >= 0; i--) { ys = new Cell(xs[i], ys); } return ys; } // リストの連結 public Cell Append(Cell ys) { if (Null(this)) return ys; else return new Cell(Car, Cdr.Append(ys)); } // リストの反転 public Cell Reverse() { var ys = nil; foreach(object x in this) { ys = new Cell(x, ys); } return ys; } // リストの長さ public int Length() { int c = 0; foreach(object x in this) { c++; } return c; } // 参照 public object Nth(int n) { var xs = this; while (!Null(xs)) { if (n-- == 0) return xs.Car; xs = xs.Cdr; } throw new IndexOutOfRangeException(); } // 探索 public Cell Member(object x) { var xs = this; while (!Null(xs)) { if (Eql(xs.Car, x)) return xs; xs = xs.Cdr; } return xs; } public int IndexOf(object x) { int i = 0; foreach(object y in this) { if (Eql(x, y)) return i; i++; } return -1; } public int FindIndex(Func<object, bool> pred) { int i = 0; foreach(object x in this) { if (pred(x)) return i; i++; } return -1; } // マッピング public Cell Map(Func<object, object> func) { if (Null(this)) return nil; else return new Cell(func(Car), Cdr.Map(func)); } // フィルター public Cell Filter(Func<object, bool> pred) { if (Null(this)) return nil; else if (pred(Car)) return new Cell(Car, Cdr.Filter(pred)); else return Cdr.Filter(pred); } // 畳み込み public object FoldLeft(Func<object, object, object> func, object a) { foreach(object x in this) { a = func(a, x); } return a; } public object FoldRight(Func<object, object, object> func, object a) { if (Null(this)) return a; else return func(Car, Cdr.FoldRight(func, a)); } // 巡回 public void Each(Action<object> func) { foreach(object x in this) { func(x); } } // 連想リスト public Cell Zip(Cell xs) { if (Null(this) || Null(xs)) return nil; else return new Cell(List(Car, xs.Car), Cdr.Zip(xs.Cdr)); } public Cell Assoc(object item) { foreach(object xs in this) { Cell ys = (Cell)xs; if (Eql(item, ys.Car)) return ys; } return nil; } // 表示 public void Print() { var xs = this; Console.Write("("); while (!Null(xs)) { if (Consp(xs.Car)) { Cell cp = (Cell)xs.Car; cp.Print(); } else { Console.Write("{0}", xs.Car); } xs = xs.Cdr; if (!Null(xs)) Console.Write(" "); } Console.Write(")"); } public void PrintLine() { this.Print(); Console.WriteLine(""); } } void test() { var a = List(1, 2); var b = List(3, 4, 5); var c = List(6, 7, 8, 9); var d = List(a, b, c); a.PrintLine(); b.PrintLine(); c.PrintLine(); d.PrintLine(); var e = a.Append(b).Append(c); e.PrintLine(); e.Reverse().PrintLine(); Console.WriteLine("{0}", e.Length()); foreach(object x in e) { Console.Write("{0} ", x); } Console.WriteLine(""); for (int i = 0; i < e.Length(); i++) { Console.Write("{0} ", e.Nth(i)); } Console.WriteLine(""); d.Map(xs => ((Cell)xs).Length()).PrintLine(); e.Map(n => (int)n * 10).PrintLine(); d.Map(xs => ((Cell)xs).Reverse()).PrintLine(); e.Filter(n => (int)n % 2 == 0).PrintLine(); Console.WriteLine("{0}", e.FoldLeft((sum, n) => (int)sum + (int)n, 0)); Console.WriteLine("{0}", e.FoldRight((n, sum) => (int)sum + (int)n, 0)); e.Each(n => Console.Write("{0} ", n)); Console.WriteLine(""); for (int i = 0; i <= 10; i++) { e.Member(i).PrintLine(); Console.WriteLine("{0}", e.IndexOf(i)); Console.WriteLine("{0}", e.FindIndex(n => (int)n == i)); } var alist = List("foo", "bar", "baz").Zip(List(1, 2, 3)); alist.PrintLine(); alist.Assoc("foo").PrintLine(); alist.Assoc("bar").PrintLine(); alist.Assoc("baz").PrintLine(); alist.Assoc("oops").PrintLine(); } // テスト test();
$ dotnet script linklist3.csx (1 2) (3 4 5) (6 7 8 9) ((1 2) (3 4 5) (6 7 8 9)) (1 2 3 4 5 6 7 8 9) (9 8 7 6 5 4 3 2 1) 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 (2 3 4) (10 20 30 40 50 60 70 80 90) ((2 1) (5 4 3) (9 8 7 6)) (2 4 6 8) 45 45 1 2 3 4 5 6 7 8 9 () -1 -1 (1 2 3 4 5 6 7 8 9) 0 0 (2 3 4 5 6 7 8 9) 1 1 (3 4 5 6 7 8 9) 2 2 (4 5 6 7 8 9) 3 3 (5 6 7 8 9) 4 4 (6 7 8 9) 5 5 (7 8 9) 6 6 (8 9) 7 7 (9) 8 8 () -1 -1 ((foo 1) (bar 2) (baz 3)) (foo 1) (bar 2) (baz 3) ()
リスト : 二分木 (ジェネリック版, tree.csx) using System.Collections; using System.Collections.Generic; class Tree<T> : IEnumerable<T> where T : IComparable<T> { // 節 class Node { public T Item { set; get; } public Node Left { set; get; } public Node Right { set; get; } public Node(T item) { Item = item; Left = null; Right = null; } } // ルート Node root; // コンストラクタ public Tree() { root = null; } // 操作関数 // 挿入 static Node InsertNode(T x, Node node) { if (node == null) return new Node(x); else { int r = x.CompareTo(node.Item); if (r < 0) node.Left = InsertNode(x, node.Left); else if (r > 0) node.Right = InsertNode(x, node.Right); return node; } } // 探索 static bool SearchNode(T x, Node node) { while (node != null) { int r = x.CompareTo(node.Item); if (r == 0) return true; else if (r < 0) node = node.Left; else node = node.Right; } return false; } // 最小値の探索 static T SearchMin(Node node) { while (node.Left != null) node = node.Left; return node.Item; } // 最小値のノードを削除 static Node DeleteMin(Node node) { if (node.Left == null) return node.Right; node.Left = DeleteMin(node.Left); return node; } // 削除 static Node DeleteNode(T x, Node node) { if (node != null) { int r = x.CompareTo(node.Item); if (r == 0) { if (node.Left == null) return node.Right; if (node.Right == null) return node.Left; node.Item = SearchMin(node.Right); node.Right = DeleteMin(node.Right); } else if (r < 0) { node.Left = DeleteNode(x, node.Left); } else if (r > 0) { node.Right = DeleteNode(x, node.Right); } } return node; } // 巡回 static void EachNode(Action<T> func, Node node) { if (node != null) { EachNode(func, node.Left); func(node.Item); EachNode(func, node.Right); } } // メソッド // 挿入 public void Insert(T x) { root = InsertNode(x, root); } // 探索 public bool Search(T x) { return SearchNode(x, root); } // 削除 public void Delete(T x) { root = DeleteNode(x, root); } // 巡回 public void Each(Action<T> func) { EachNode(func, root); } // 空の木か public bool IsEmpty() { return root == null; } // 空にする public void Clear() { root = null; } // イテレータ static void NextNode(List<Node> path, Node node) { while (node != null) { path.Add(node); node = node.Left; } } public IEnumerator<T> GetEnumerator() { var path = new List<Node>(); NextNode(path, root); while (path.Count > 0) { var node = path[path.Count - 1]; path.RemoveAt(path.Count - 1); yield return node.Item; NextNode(path, node.Right); } } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } void test() { int[] a = new int[] {5,6,4,3,7,8,1,2,9,0}; var tree = new Tree<int>(); foreach(int x in a) { tree.Insert(x); } for (int i = -1; i < 11; i++) { Console.WriteLine("{0} {1}", i, tree.Search(i)); } tree.Each(x => Console.WriteLine("{0}", x)); foreach (int x in tree) { Console.Write("{0} ", x); } Console.WriteLine(""); for (int i = 0; i < 10; i++) { tree.Delete(i); Console.WriteLine("{0} {1}", i, tree.Search(i)); tree.Each(x => Console.Write("{0} ", x)); Console.WriteLine(""); } } // テスト test();
$ dotnet script tree.csx -1 False 0 True 1 True 2 True 3 True 4 True 5 True 6 True 7 True 8 True 9 True 10 False 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 False 1 2 3 4 5 6 7 8 9 1 False 2 3 4 5 6 7 8 9 2 False 3 4 5 6 7 8 9 3 False 4 5 6 7 8 9 4 False 5 6 7 8 9 5 False 6 7 8 9 6 False 7 8 9 7 False 8 9 8 False 9 9 False
リスト : 二分木 (immutable, tree1.csx) using System.Collections; using System.Collections.Generic; class Tree<T> : IEnumerable<T> where T : IComparable<T> { public T Item { get; } public Tree<T> Left { get; } public Tree<T> Right { get; } public Tree(T item, Tree<T> left, Tree<T> right) { Item = item; Left = left; Right = right; } // 空の木 public static readonly Tree<T> nil = new Tree<T>(default(T), null, null); public bool IsEmpty() { return this == nil; } public static Tree<T> MakeTree() { return nil; } // 挿入 public Tree<T> Insert(T x) { if (this == nil) return new Tree<T>(x, nil, nil); else { int r = x.CompareTo(Item); if (r < 0) return new Tree<T>(Item, Left.Insert(x), Right); else if (r > 0) return new Tree<T>(Item, Left, Right.Insert(x)); else return this; } } // 探索 public bool Search(T x) { Tree<T> node = this; while (node != nil) { int r = x.CompareTo(node.Item); if (r == 0) return true; else if (r < 0) node = node.Left; else node = node.Right; } return false; } // 最小値の探索 public T SearchMin() { Tree<T> node = this; while (node != nil) node = node.Left; return node.Item; } // 最小値のノードを削除 public Tree<T> DeleteMin() { if (Left == nil) return Right; return new Tree<T>(Item, Left.DeleteMin(), Right); } // 削除 public Tree<T> Delete(T x) { if (this == nil) { return nil; } else { int r = x.CompareTo(Item); if (r == 0) { if (Left == nil) return Right; if (Right == nil) return Left; return new Tree<T>(Right.SearchMin(), Left, Right.DeleteMin()); } else if (r < 0) { return new Tree<T>(Item, Left.Delete(x), Right); } else { return new Tree<T>(Item, Left, Right.Delete(x)); } } } // 巡回 public void Each(Action<T> func) { if (this != nil) { Left.Each(func); func(Item); Right.Each(func); } } // イテレータ static void NextNode(List<Tree<T>> path, Tree<T> node) { while (node != nil) { path.Add(node); node = node.Left; } } public IEnumerator<T> GetEnumerator() { var path = new List<Tree<T>>(); NextNode(path, this); while (path.Count > 0) { var node = path[path.Count - 1]; path.RemoveAt(path.Count - 1); yield return node.Item; NextNode(path, node.Right); } } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } void test() { int[] a = new int[] {5,6,4,3,7,8,1,2,9,0}; var tree = Tree<int>.MakeTree(); foreach(int x in a) { tree = tree.Insert(x); } for (int i = -1; i < 11; i++) { Console.WriteLine("{0} {1}", i, tree.Search(i)); } tree.Each(x => Console.WriteLine("{0}", x)); foreach(int x in tree) { Console.Write("{0} ", x); } Console.WriteLine(""); for (int i = 0; i < 10; i++) { tree = tree.Delete(i); Console.WriteLine("{0} {1}", i, tree.Search(i)); tree.Each(x => Console.Write("{0} ", x)); Console.WriteLine(""); } } // テスト test();
$ dotnet script tree1.csx -1 False 0 True 1 True 2 True 3 True 4 True 5 True 6 True 7 True 8 True 9 True 10 False 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 False 1 2 3 4 5 6 7 8 9 1 False 2 3 4 5 6 7 8 9 2 False 3 4 5 6 7 8 9 3 False 4 5 6 7 8 9 4 False 5 6 7 8 9 5 False 6 7 8 9 6 False 7 8 9 7 False 8 9 8 False 9 9 False
皆さんお馴染みの「8パズル」を幅優先探索で解くプログラムです。詳しい説明は拙作のページ C言語超入門: 「スライドパズル」をお読みください。
// // eight.csx : 8 パズル (幅優先探索) // // Copyright (C) 2016-2022 Makoto Hiroi // using System.Collections.Generic; class State { public int space; public int[] board; public State prev; public State(int s, int[] b, State st) { space = s; board = b; prev = st; } } // 隣接リスト // 0 1 2 // 3 4 5 // 6 7 8 const int Size = 9; int[][] adjacent = new int[Size][] { new int[] {1, 3}, new int[] {0, 2, 4}, new int[] {1, 5}, new int[] {0, 4, 6}, new int[] {1, 3, 5, 7}, new int[] {2, 4, 8}, new int[] {3, 7}, new int[] {4, 6, 8}, new int[] {5, 7}, }; // キュー const int MaxSize = 181440; State[] que = new State[MaxSize]; int rear; int front; void enq(State st) { que[rear++] = st; } State deq() { return que[front++]; } bool IsEmpty() { return rear == front; } bool EqualBoard(int[] xs, int[] ys) { for (int i = 0; i < Size; i++) if (xs[i] != ys[i]) return false; return true; } int[] CopyBoard(int[] xs) { int[] ys = new int[Size]; for (int i = 0; i < Size; i++) ys[i] = xs[i]; return ys; } int BoardToInt(int[] xs) { int a = 0; foreach(int x in xs) { a = a * 10 + x; } return a; } void PrintAnswer(State st) { if (st.prev != null) PrintAnswer(st.prev); foreach(int x in st.board) { Console.Write("{0} ", x); } Console.WriteLine(""); } void bfs(int[] start, int[] goal) { var ht = new Dictionary<int, bool>(); enq(new State(Array.IndexOf(start, 0), start, null)); ht[BoardToInt(start)] = true; while (!IsEmpty()) { State st = deq(); int s = st.space; int[] b = st.board; if (EqualBoard(b, goal)) { PrintAnswer(st); return; } else { foreach(int x in adjacent[s]) { int[] newBoard = CopyBoard(b); newBoard[s] = newBoard[x]; newBoard[x] = 0; int key = BoardToInt(newBoard); if (!ht.ContainsKey(key)) { ht[key] = true; enq(new State(x, newBoard, st)); } } } } } // 実行 bfs(new int[] {8,6,7,2,5,4,3,0,1}, new int[] {1,2,3,4,5,6,7,8,0});
$ dotnet script eight.csx 8 6 7 2 5 4 3 0 1 8 6 7 2 0 4 3 5 1 8 0 7 2 6 4 3 5 1 0 8 7 2 6 4 3 5 1 2 8 7 0 6 4 3 5 1 2 8 7 3 6 4 0 5 1 2 8 7 3 6 4 5 0 1 2 8 7 3 6 4 5 1 0 2 8 7 3 6 0 5 1 4 2 8 0 3 6 7 5 1 4 2 0 8 3 6 7 5 1 4 2 6 8 3 0 7 5 1 4 2 6 8 0 3 7 5 1 4 2 6 8 5 3 7 0 1 4 2 6 8 5 3 7 1 0 4 2 6 8 5 3 7 1 4 0 2 6 8 5 3 0 1 4 7 2 6 0 5 3 8 1 4 7 2 0 6 5 3 8 1 4 7 2 3 6 5 0 8 1 4 7 2 3 6 0 5 8 1 4 7 2 3 6 1 5 8 0 4 7 2 3 6 1 5 8 4 0 7 2 3 6 1 5 8 4 7 0 2 3 6 1 5 0 4 7 8 2 3 0 1 5 6 4 7 8 2 0 3 1 5 6 4 7 8 0 2 3 1 5 6 4 7 8 1 2 3 0 5 6 4 7 8 1 2 3 4 5 6 0 7 8 1 2 3 4 5 6 7 0 8 1 2 3 4 5 6 7 8 0
皆さんお馴染みの「8パズル」を反復深化と下限値枝刈り法で解くプログラムです。詳しい説明は拙作のページ C言語超入門: 「スライドパズル (2)」をお読みください。
// // eight1.csx : 8 パズル (反復深化+下限値枝刈り法) // // Copyright (C) 2016-2022 Makoto Hiroi // using System.Collections.Generic; // 隣接リスト // 0 1 2 // 3 4 5 // 6 7 8 const int Size = 9; static int[][] adjacent = new int[Size][] { new int[] {1, 3}, new int[] {0, 2, 4}, new int[] {1, 5}, new int[] {0, 4, 6}, new int[] {1, 3, 5, 7}, new int[] {2, 4, 8}, new int[] {3, 7}, new int[] {4, 6, 8}, new int[] {5, 7}, }; // 移動距離 static int[,] distance = { {0, 0, 0, 0, 0, 0, 0, 0, 0}, // dummy {0, 1, 2, 1, 2, 3, 2, 3, 4}, {1, 0, 1, 2, 1, 2, 3, 2, 3}, {2, 1, 0, 3, 2, 1, 4, 3, 2}, {1, 2, 3, 0, 1, 2, 1, 2, 3}, {2, 1, 2, 1, 0, 1, 2, 1, 2}, {3, 2, 1, 2, 1, 0, 3, 2, 1}, {2, 3, 4, 1, 2, 3, 0, 1, 2}, {3, 2, 3, 2, 1, 2, 1, 0, 1}, }; // 解の総数 int count; // 移動距離の計算 int CalcDistance(int[] board) { int d = 0; for (int i = 0; i < Size; i++) { int p = board[i]; d += distance[p, i]; } return d; } bool EqualBoard(int[] xs, int[] ys) { for (int i = 0; i < Size; i++) if (xs[i] != ys[i]) return false; return true; } void dfs(int[] board, int s, int[] goal, int limit, int low, List<int> move) { if (move.Count - 1 == limit) { if (EqualBoard(board, goal)) { count++; foreach(int x in move) { Console.Write("{0} ", x); } Console.WriteLine(""); } } else { foreach(int x in adjacent[s]) { int p = board[x]; if (p == move[move.Count - 1]) continue; int newLow = low - distance[p, x] + distance[p, s]; if (newLow + move.Count - 1 <= limit) { board[s] = p; board[x] = 0; move.Add(p); dfs(board, x, goal, limit, newLow, move); move.RemoveAt(move.Count - 1); board[x] = p; board[s] = 0; } } } } void idsearch() { var start = new int[] {8,6,7,2,5,4,3,0,1}; var goal = new int[] {1,2,3,4,5,6,7,8,0}; var move = new List<int>(); move.Add(0); // dummy int low = CalcDistance(start); for(int limit = low; limit < 32; limit++) { Console.WriteLine("----- {0} -----", limit); dfs(start, 7, goal, limit, low, move); if (count > 0) break; } Console.WriteLine("count = {0}", count); } // 実行 idsearch();
$ dotnet script eight1.csx ----- 21 ----- ----- 22 ----- ----- 23 ----- ----- 24 ----- ----- 25 ----- ----- 26 ----- ----- 27 ----- ----- 28 ----- ----- 29 ----- ----- 30 ----- ----- 31 ----- 0 5 6 8 2 3 5 1 4 7 8 6 3 5 1 4 7 8 6 3 5 1 4 7 8 6 3 2 1 4 7 8 0 5 6 7 4 6 2 3 5 1 6 2 3 8 7 4 2 3 1 5 8 7 4 1 5 8 7 4 1 2 3 6 0 5 2 3 5 1 4 7 6 8 3 2 8 3 2 5 1 4 7 8 5 1 4 7 8 6 3 2 1 4 7 8 ・・・ 省略 ・・・ 0 1 4 5 2 3 1 4 5 7 6 8 3 2 8 3 2 1 4 5 7 8 5 7 8 6 3 2 1 4 7 8 0 1 4 5 2 3 1 4 5 7 6 2 3 8 2 3 8 1 4 8 7 5 8 7 5 6 3 2 1 4 7 8 0 1 4 5 2 3 1 4 5 7 6 2 3 8 2 3 8 1 4 5 7 8 5 7 8 6 3 2 1 4 7 8 count = 40