M.Hiroi's Home Page

C# Programming

お気楽C#プログラミング超入門

[ Home | C# ]

簡単なプログラム

●連結リスト

片方向の連結リスト (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.cs)

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 な連結リスト

リスト : immutable な連結リスト (ジェネリック版, linklist2.cs)

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 ライクな連結リスト

リスト : 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)
()

●二分木 (mutable)

リスト : 二分木 (ジェネリック版, tree.cs)

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)

リスト : 二分木 (immutable, tree1.cs)

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パズル (幅優先探索)

皆さんお馴染みの「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パズル (反復深化)

皆さんお馴染みの「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

初版 2016 年 9, 10 月
改訂 2022 年 2 月 12 日

Copyright (C) 2016-2022 Makoto Hiroi
All rights reserved.

[ Home | C# ]