Инкрементальные алфавиты

Я пытаюсь создать функцию, которая даст мне позицию в алфавите при передаче индекса. Это будет похоже на то, как Excel показывает столбцы. A ... Z, AA, AB .... Я написал функцию ниже, чтобы получить результаты до Z. Похоже,

static string GetColumnName(int index)
{
    const int alphabetsCount = 26;
    if (index <= alphabetsCount)
    {
        int code = (index - 1) + (int)'A';
        return char.ConvertFromUtf32(code);
    }
    return string.Empty;
}

Это нормально работает до "Z". Он возвращает «A», если я передаю 1, и возвращаю «B», если я прохожу 2, и так далее. Но я не могу понять, как я получу AA, если передам 27 этой функции. Думаю, мне нужен рекурсивный метод, чтобы найти его.

Любой вклад в эту проблему будет отличным!

Изменить

Это предполагает Тордек. Но его код не работает с числами вроде 52, 78 и т. Д. Добавлен обходной путь для этого, и вот окончательный рабочий код.

static string GetColumnName(int index)
{
    const int alphabetsCount = 26;

    if (index > alphabetsCount)
    {
        int mod = index % alphabetsCount;
        int columnIndex = index / alphabetsCount;

        // if mod is 0 (clearly divisible) we reached end of one combination. Something like AZ
        if (mod == 0)
        {
            // reducing column index as index / alphabetsCount will give the next value and we will miss one column.
            columnIndex -= 1;
            // passing 0 to the function will return character '@' which is invalid
            // mod should be the alphabets count. So it takes the last char in the alphabet.
            mod = alphabetsCount;
        }
        return GetColumnName(columnIndex) + GetColumnName(mod);
    }
    else
    {
        int code = (index - 1) + (int)'A';
        return char.ConvertFromUtf32(code);
    }
}

person Navaneeth K N    schedule 29.05.2009    source источник


Ответы (5)


Любую рекурсивную функцию можно преобразовать в эквивалентную итеративную. Мне всегда легко сначала подумать рекурсивно:

static string GetColumnName(int index)
{
    const int alphabetsCount = 26;

    if (index > alphabetsCount) {
        return GetColumnName(index / alphabetsCount) + GetColumnName(index % alphabetsCount);
    } else {
        int code = (index - 1) + (int)'A';
        return char.ConvertFromUtf32(code);
    }
}

Что можно просто преобразовать в:

static string GetColumnName(int index)
{
    const int alphabetsCount = 26;
    string result = string.Empty;

    while (index > 0) {
        result = char.ConvertFromUtf32(64 + (index % alphabetsCount)) + result;
        index /= alphabetsCount;
    }

    return result;
}

Тем не менее, послушайте Джоэла.

person Tordek    schedule 29.05.2009
comment
Здорово. Моя математика ржавая. Так что не мог понять операцию по модулю. Спасибо за код. - person Navaneeth K N; 29.05.2009

См. Этот вопрос:
Преобразование индекса столбца в имя столбца Excel

или этот:
Как преобразовать номер столбца (например, 127) в столбец Excel (например, AA)

Хотя первая ссылка имеет правильный ответ прямо вверху, а вторая - несколько неправильных.

person Joel Coehoorn    schedule 29.05.2009

Рекурсия - одна из возможностей - если index > 26, вы имеете дело с index % 26 в этом вызове и объединяете его с рекурсивным вызовом index / 26. Однако итерация часто бывает более быстрой, и ее несложно организовать для таких простых случаев, как этот. В псевдокоде:

string result = <convert `index % 26`>
while index > 26:
  index = index / 26
  result = <convert `index % 26`> + result
return result

или тому подобное.

person Alex Martelli    schedule 29.05.2009
comment
В вашем псевдокоде нет ничего плохого, но важно знать, что вам, вероятно, следует использовать класс StringBuilder, если вы собираетесь добавлять к строке в цикле, чтобы сэкономить на распределении объектов: msdn.microsoft.com/en-us/library/ - person Paul Fisher; 29.05.2009
comment
Это неверно, потому что это не система с основанием 26. Если бы A было бы 0, A было бы AA (0 == 00). Если бы A было равно 1, переход от Z к AA был бы подобен переходу от 9 к 11. - person Daniel Brückner; 29.05.2009
comment
Пол, StringBuilder перебор. 32-битное int - это 2 ^ 32, а 26 ^ 7 переполняет его, поэтому максимальная длина составляет 7 букв. 7 итераций - это совсем не сложно. В 64-битном формате будет всего 14 букв. :) - person Colin Burnett; 29.05.2009

static string GetColumnName(int index)
{
    const int alphabetsCount = 26;
    string result = '';

    if (index >= alphabetsCount)
    {
        result += GetColumnName(index-alphabetsCount)
    }
    return (string) (64 + index);
}

Мой C # УЖАСНЫЙ И РЖАВНЫЙ. Интерпретируйте это как псевдокод - он почти наверняка не будет компилироваться, но может помочь вам начать.

person Anonymous Cow    schedule 29.05.2009

Я не хочу отвечать на этот вопрос на C #, но я собираюсь показать вам, насколько это просто в Haskell.

alphas :: [String]
alphas = [x ++ [c] | x <- ([]:alphas), c <- ['A'..'Z']]

Prelude> take 100 alphas
["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T",
 "U","V","W","X","Y","Z","AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK",
 "AL","AM","AN","AO","AP","AQ","AR","AS","AT","AU","AV","AW","AX","AY","AZ","BA",
 "BB","BC","BD","BE","BF","BG","BH","BI","BJ","BK","BL","BM","BN","BO","BP","BQ",
 "BR","BS","BT","BU","BV","BW","BX","BY","BZ","CA","CB","CC","CD","CE","CF","CG",
 "CH","CI","CJ","CK","CL","CM","CN","CO","CP","CQ","CR","CS","CT","CU","CV"]
person Dietrich Epp    schedule 29.05.2009