電波ビーチ

☆(ゝω・)v

2次元配列のリストの中に特定の2次元配列が含まれているかどうかをいい感じに高速に判定するにはどうすればいいですか

むかし書いたやつが発掘されたので供養というか。すっかりC#忘れてるので読解に苦労しました。むかしの自分すげぇな

using System;
using System.Linq;
using System.Collections.Generic;

public class Program{
    public static void Main(){
        
        //はじめに
        var test1 = new int[,]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        var test2 = test1.Clone() as int[,];     //test1と同値をもつ2次元配列
        var set1 = new HashSet<int[,]>();
        set1.Add(test1);
        Console.WriteLine(set1.Contains(test2));    //test1とtest2はそもそも別オブジェクトなので当然
        
        var unziped1 = UnZip(test1);    //一次元配列に展開
        var unziped2 = UnZip(test2);    //同じく
        var set2 = new HashSet<int[]>();
        set2.Add(unziped1);
        Console.WriteLine(set2.Contains(unziped2));  //当然
        
        ///////////////////////////////////////////////////
        //解法1
        //値型に何らかの形で直してやるパターン
        //文字列
        var str1 = ToStr(test1);
        var str2 = ToStr(test2);
        var set3 = new HashSet<string>();
        set3.Add(str1);
        Console.WriteLine(set3.Contains(str2));     //文字列に直せばいいけどstringへの変換がクソ遅い
        
        //longとかそのへん
        var longed1 = ToLong(test1);
        var longed2 = ToLong(test2);
        var set4 = new HashSet<long>();
        set4.Add(longed1);
        Console.WriteLine(set4.Contains(longed2));  //文字列よりはマシだけど一桁ずつに情報をもたせているので9種類しか持てないし限定的すぎる(1と11が含まれていたらもう復元できない・0が先頭になることはない)
                                                    //あるいは数値とインデックスでHashCode的なのをうまく一意に生成できれば可能っぽいけど
        
        //////////////////////////////////////////////////
        //解法2
        //愚直に舐めていくパターン
        set1.Clear();
        bool flag;
        var _any1 = new int[,]{{2, 3, 4}, {5, 6, 7}, {8, 9, 1}};
        var _any2 = new int[,]{{3, 4, 5}, {6, 7, 8}, {9, 1, 2}};
        var _any3 = new int[,]{{9, 8, 7}, {6, 5, 4}, {3, 2, 1}};
        //...このあと_any1000000000くらいまで続くと想定
        set1.Add(_any1);
        set1.Add(_any2);
        set1.Add(_any3);
        //...このあと_any1000000000くらいまで続く想定
        set1.Add(test1);
        
        //set1にtest2と同じ要素をもつものが含まれているかどうか調べたい
        var b = false;
        foreach(var s in set1){
            if(Check(s, test2)){
                b = true;
                break;
            }
        }
        Console.WriteLine(b ? "ありました" : "ねぇよ帰れクソが");
        //この方法だと見つけたいターゲットがリストの最後のほうにあったときに探索に時間がかかる。Linqもゴテゴテなのでたぶん遅い
    }
    
    public static int[] UnZip(int[,] moto){
        return moto.Cast<int>().ToArray();
        var h = moto.GetLength(0);
        var w = moto.GetLength(1);
        var len = h*w;
        var ret = new int[len];
        for(int i=0; i<h; i++){
            for(int j=0; j<w; j++){
                ret[i*w+j] = moto[i,j];
            }
        }
        return ret;
    }
    
    public static string ToStr(int[,] moto){
        var h = moto.GetLength(0);
        var w = moto.GetLength(1);
        var sb = new System.Text.StringBuilder();
        for(int i=0; i<h; i++){
            for(int j=0; j<w; j++){
                sb.Append(moto[i,j].ToString());
            }
        }
        return sb.ToString();
    }
    
    public static long ToLong(int[,] moto){
        var h = moto.GetLength(0);
        var w = moto.GetLength(1);
        long ret = default(long);
        for(int i=0; i<h; i++){
            for(int j=0; j<w; j++){
                var count = i*h+j;
                ret+=moto[i,j]*(long)Math.Pow(10, h*w-count-1);
            }
        }
        return ret;
    }
    
    
    public static bool Check(int[,] me, int[,] target){
        //meとtargetが同じ次元数かつ同じ要素数であるか
        var unko = me.Rank==target.Rank&&Enumerable.Range(0, me.Rank).All(dim=>me.GetLength(dim)==target.GetLength(dim));
        if(!unko) return false;
        return me.Cast<int>().SequenceEqual(target.Cast<int>());
    }
}