Судоку как CSP - Поиск с возвратом

ОГЛАВЛЕНИЕ

Поиск с возвратом

Используя поиск с возвратом, можно проходить сквозь  деревья и проверять любую вероятность (но если мы умные, нам не нужно это делать) на недействительность. Но из-за ограничений каждое место становится все меньше и меньше, и если мы будем использовать какую-либо эвристику, то сделаем это намного лучше!

Поиск с возвратом происходит примерно так: если уже есть верное решение, это решение возвращается; если верного решения нет, выбирается незаполненная ячейка, и рассматриваются все ее возможные значения (в каком порядке?). Поодиночке мы пытаемся присвоить значение каждой ячейке и продолжаем искать от результирующей позиции.

MyCell[,] backtrackingSearch(MyCell[,] branch)
{
    MyCell[,] ret;

    if (branch == null) return null;
    if (isFinish(branch)) return branch;

    Pos s = SelectUnassignedVariable(branch);

    while (branch[s.ln, s.col].Value.Length > 0)
    {

        char c = SelectDomainValue(branch, s);

        ret = backtrackingSearch(Assign(Clone(branch), s, c));

        if (ret != null)
            return ret;

        m_NumOfBacktracking++;
        branch = Eliminate(branch, s, c);

        if (branch == null) return null;
    }

Эвристика переменной (Или: какую ячейку нужно проверить первой?)

В вышеупомянутом алгоритме поиска мы используем функцию SelectUnassignedVariable, но она не сообщает ничего о критерии, потому что данная 'функция’  является делегатом, предназначенным для сравнения с какой-либо другой эвристикой.  Напишем обобщенный (групповой) поиск с возвратом, просто изменяя значение делегата с одного на другое. Наша программа реализует только классическую эвристику.

Первое неопределенное значение

Здесь используется самая простая эвристика. Это очень быстро, но, с другой стороны, очень глупо. Мы просто выбираем первое значение, которое находим, и используем его для поиска с помощью простого перебора, однако хотелось бы сделать что-то более серьезное.

Pos FirstUnassignedCell(MyCell[,] branch)
{
    for (int i = 0; i < m_gridSize; i++)
        for (int j = 0; j < m_gridSize; j++)
            if (!branch[i, j].assigned)
                return new Pos(i, j);
    return null;
}

Эвристика минимального остающегося значения (MRV):

В данном случае выбирается ячейка с минимальной вероятностью. Это хорошо, потому что есть большой шанс правильного угадывания. Это также уменьшает коэффициент ветвления.

Pos MinimumRemainingValues(MyCell[,] branch)
{
    int min = m_gridSize + 1;
    Pos p=new Pos() ;

    for (int i = 0; i < m_gridSize; i++)
        for (int j = 0; j < m_gridSize; j++)
        {
            if ((!branch[i, j].assigned) &&
                (branch[i, j].Value.Length < min))
            {
                p.ln = i;
                p.col = j;
                min = branch[i, j].Value.Length;
            }
        }
    return p;
}

MRV + Максимальный порядок (MRV+MD)

У нас есть MRV, и это очень хорошо, но, может быть, можно сделать все еще лучше. Мы все еще используем MRV, но теперь как разрушитель связей. Мы выбираем ячейку с максимальным порядком (степенью). Порядок ячейки – это число неопределенных ячеек, на которые данная ячейка накладывает ограничение.

Pos MRV_With_MD(MyCell[,] branch)
{
    return MaxDegree(branch, MinimumRemainingValuesList(branch));
}

/// <summary>
/// получаем список минимальных остающихся значений позиций переменных.
/// </summary>
/// <param name="branch">Ветвь.</param>
/// <returns></returns>
List<Pos> MinimumRemainingValuesList(MyCell[,] branch)
{
    int min = m_gridSize + 1;
    List<Pos> list = new List<Pos>();
    for (int i = 0; i < m_gridSize; i++)
        for (int j = 0; j < m_gridSize; j++)
        {
            if ((!(branch[i, j].assigned)) && (branch[i, j].Value.Length == min))
            {
                list.Add(new Pos(i, j));
                continue;
            }
            if ((!(branch[i, j].assigned))&& (branch[i, j].Value.Length < min))
            {
                list.Clear();
                min = branch[i, j].Value.Length;
                list.Add(new Pos(i, j));
            }

        }
    return list;
}

/// <summary>
/// получаем позицию переменной с максимальным порядком из позиций в MRVS.
/// </summary>
/// <param name="branch">Ветвь.</param>
/// <param name="MRVS">MRVS.</param>
/// <returns></returns>
Pos MaxDegree(MyCell [,]branch, List<Pos> MRVS)
{
    int deg = -1;
    Pos p =null;
    for (int i = 0; i < MRVS.Count; i++)
    {
        int count = 0;
        for (int k = 0; k < branch[MRVS[i].ln ,MRVS[i].col].Peers.Count; k++)
            if (!branch[branch[MRVS[i].ln ,MRVS[i].col].Peers[k].ln,
                     branch[MRVS[i].ln ,MRVS[i].col].Peers[k].col].assigned)
                count++;
        if (count > deg)
        {
            deg = count;
            p = MRVS[i];
        }
    }
    return p;
}

Это выглядит лучше, но требует много времени на вычисления, так стоит ли использовать этот метод? Не всегда. Мы выполним ряд сравнений в конце.