Как я могу отсортировать номера лексикографически?

голоса
12

Вот сценарий.

Я даюсь массив «А» целых чисел. Размер массива не является фиксированным. Функция, которую я должен написать, можно назвать один раз с массивом всего несколько целых чисел, а в другое время, может даже содержать тысячи целых чисел. Кроме того, каждое целое число не должно содержать такое же количество цифр.

Я должен «рода» числа в массиве таким образом, что результирующий массив имеет целые числа упорядоченные в лексикографическом порядке (т.е. они сортируются на основе их строковых представлений. Здесь «123» является строковым представлением 123). Обратите внимание, что вывод должен содержать только целые числа, а не их строковые эквиваленты.

Например: если на входе:

[12 | 2434 | 23 | 1 | 654 | 222 | 56 | 100000]

Тогда на выходе должно быть:

[1 | 100000 | 12 | 222 | 23 | 2434 | 56 | 654]

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

Я привел полагать, что решение не нуждается обивка и т.д., и есть простое решение, в котором вы просто должны обрабатывать числа в некотором роде (некоторые немного обработки?), Чтобы получить результат.

Что такое пространство-накрест наиболее эффективное решение, которое вы можете думать? Время мудр?

Если вы даете код, я предпочел бы Java или псевдо-код. Но если это не устраивает вас, любой такой язык должен быть в порядке.

Задан 19/05/2009 в 14:59
источник пользователем
На других языках...                            


14 ответов

голоса
9

Исполняемые псевдо-код (ака Python): thenumbers.sort(key=str). Да, я знаю , что с помощью Python вроде как обман - это просто слишком мощный ;-). А если серьезно, это также означает: если вы можете сортировать массив строк лексически, так как в Python сортировать по своей природе может, то просто сделать «ключевую строку» из каждого числа и рода , что вспомогательный массив (вы можете восстановить нужный массив чисел, STR - > Int преобразования, или делая вид на индексы с помощью косвенности, и т.д. и т.п.); это известно как ДГ (Украсить, сортировку, Undecorate) , и это то , что key=аргумент сортировка реализует Python.

Более подробно (псевдокод):

  1. выделить массив гольца ** до auxтех пор , как numbersмассив
  2. для я от 0 до length of numbers-1,aux[i]=stringify(numbers[i])
  3. выделить массив Int indicesтой же длины
  4. для я от 0 до length of numbers-1,indices[i]=i
  5. сортировать indices, используя в качествеcmp(i,j) strcmp(aux[i],aux[j])
  6. выделить массив Int resultsтой же длины
  7. для я от 0 до length of numbers-1,results[i]=numbers[indices[i]]
  8. тетсру resultsнадnumbers
  9. бесплатно каждый aux[i], а также aux, indices,results
Ответил 19/05/2009 в 15:02
источник пользователем

голоса
2

Мое искушение было бы сказать, что ИНТ для преобразования строки будет происходить в коде, а не устройство сравнения навалом. Хотя это может быть более элегантным с кодовой точки зрения я должен был бы сказать, что попытка выполнения может быть больше, поскольку каждый номер можно сравнить несколько раз.

Я был бы склонен создать новый массив, содержащий как Int и строковое представление (не уверен, что вам нужно раздуть струнные версии для сравнения строк, чтобы произвести заказ вы дали), что-то на строку, а затем скопировать прерывания INT значения обратно в исходный массив.

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

Ответил 19/05/2009 в 15:08
источник пользователем

голоса
3

Я просто превратить их в строки, а затем сортировать затем отсортировать с помощью STRCMP, который делает ЛЕКС сравнения.

В качестве альтернативы вы можете написать функцию «lexcmp», которая сравнивает два числа, используя% 10 и / 10, но это в основном то же самое, как вызов atoi много раз, так что не очень хорошая идея.

Ответил 19/05/2009 в 15:08
источник пользователем

голоса
0

Если вы собираетесь на пространство-накрест эффективность, я хотел бы попробовать просто делать работу в функции сравнения в этом роде

int compare(int a, int b) {
   // convert a to string
   // convert b to string
   // return -1 if a < b, 0 if they are equal, 1 if a > b
}

Если это слишком медленно (это медленнее, чем предварительная обработка, конечно), следить за превращениями где-то так, что функция сравнения не держит того, чтобы сделать их.

Ответил 19/05/2009 в 15:12
источник пользователем

голоса
2

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

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

Попробуйте сделать строку сериализации и сравнение в функции сравнения и эталоном этого. Я думаю, что это будет очень хорошим решением. Пример Java-МОГ псевдокод:

public static int compare(Number numA, Number numB) {
    return numA.toString().compare(numB.toString());
}

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

Ответил 19/05/2009 в 15:14
источник пользователем

голоса
3

Фактическая сортировка может быть сделана с помощью любого алгоритма вы хотите. Ключ к этой проблеме заключается в нахождении функции сравнения, которая будет правильно определить, какие цифры должны быть «меньше, чем» другие, согласно этой схеме:

bool isLessThan(int a, int b)
{
    string aString = ToString(a);
    string bString = ToString(b);

    int charCount = min(aString.length(), bString.length())
    for (charIndex = 0; charIndex < charCount; charIndex++)
    {
        if (aString[charIndex] < bString[charIndex]) { return TRUE; }
    }

    // if the numbers are of different lengths, but identical
    // for the common digits (e.g. 123 and 12345)
    // the shorter string is considered "less"
    return (aString.length() < bString.length());
}
Ответил 19/05/2009 в 15:15
источник пользователем

голоса
0

Возможная оптимизация: Вместо этого:

Я преобразовал каждое целое число в его формат строки, а затем добавляют нули справа, чтобы все числа содержат одинаковое количество цифр

вы можете умножить каждое число (10 ^ N - log10 (число)), N означает число больше, чем log10 любых из ваших чисел.

Ответил 19/05/2009 в 15:19
источник пользователем

голоса
0
#!/usr/bin/perl

use strict;
use warnings;

my @x = ( 12, 2434, 23, 1, 654, 222, 56, 100000 );

print $_, "\n" for sort @x;

__END__

Некоторые тайминги ... Во-первых, с пустым @x:

C:\Temp> timethis s-empty
TimeThis :  Elapsed Time :  00:00:00.188

Теперь, с 10000 случайно сгенерированных элементов:

TimeThis :  Elapsed Time :  00:00:00.219

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

Таким образом, сэкономить время программиста ;-)

Ответил 19/05/2009 в 15:20
источник пользователем

голоса
4

Поскольку вы упомянули Java фактического языка вопроса:

Вам не нужно конвертировать и из строк. Вместо этого, определить свой собственный компаратор и использовать его в этом роде.

В частности:

Comparator<Integer> lexCompare = new Comparator<Integer>(){
   int compareTo( Integer x, Integer y ) {
      return x.toString().compareTo( y.toString() );
   }
};

Затем вы можете отсортировать массив как это:

int[] array = /* whatever */;
Arrays.sort( array, lexCompare );

(Примечание: int/ Integerнесовпадение работает автоматически через авто-бокс)

Ответил 19/05/2009 в 15:25
источник пользователем

голоса
1

псевдокод:

sub sort_numbers_lexicographically (array) {
    for 0 <= i < array.length:
        array[i] = munge(array[i]);
    sort(array);  // using usual numeric comparisons
    for 0 <= i < array.length:
        array[i] = unmunge(array[i]);
}

Итак, что mungeи unmunge?

mungeотличаются в зависимости от размера целого числа. Например:

sub munge (4-bit-unsigned-integer n) {
    switch (n):
        case 0:  return 0
        case 1:  return 1
        case 2:  return 8
        case 3:  return 9
        case 4:  return 10
        case 5:  return 11
        case 6:  return 12
        case 7:  return 13
        case 8:  return 14
        case 9:  return 15
        case 10:  return 2
        case 11:  return 3
        case 12:  return 4
        case 13:  return 5
        case 14:  return 6
        case 15:  return 7
}

Esentially , что munge делает, говоря , что порядок 4 битовые целые числа приходят при сортировке lexigraphically. Я уверен , что вы можете видеть , что есть образец здесь --- я не должен использовать переключатель --- и что вы можете написать версию , mungeкоторая обрабатывает 32 - битные целые числа достаточно легко. Подумайте о том , как можно было бы написать версии mungeдля 5, 6 и 7 разрядных целых чисел , если вы не можете сразу увидеть картину.

unmungeявляется обратным munge.

Таким образом, вы можете избежать преобразования ничего в строку --- вам не нужна никакая дополнительная память.

Ответил 19/05/2009 в 15:35
источник пользователем

голоса
1

Если вы хотите попробовать лучшее препроцессирование-сортировку-постобработку, то обратите внимание, что ИНТ не более 10 десятичных цифр (без учета подписанного-Несса на данный момент).

Таким образом, двоично-десятичный данные для него помещается в 64 бит. Карта цифры 0-> 1, 1-> 2 и т.д., а также использовать 0 в качестве терминатора NUL (для того, чтобы «1» выходит меньше, чем «10»). Сдвиг каждой цифры, в свою очередь, начиная с самым маленьким, в верхней части долго. Сортировка лонги, который выйдет в лексикографическом порядке для оригинальных Интсов. Затем преобразовать обратно путем сдвига цифр по одному обратно из верхней части каждой длиной:

uint64_t munge(uint32_t i) {
    uint64_t acc = 0;
    while (i > 0) {
        acc = acc >> 4;
        uint64_t digit = (i % 10) + 1;
        acc += (digit << 60);
        i /= 10;
    }
    return acc;
}

uint32_t demunge(uint64_t l) {
    uint32_t acc = 0;
    while (l > 0) {
        acc *= 10;
        uint32_t digit = (l >> 60) - 1;
        acc += digit;
        l << 4;
    }
}

Или что-то типа того. Поскольку Java не имеет неподписанные Интсов, вы должны были бы изменить его немного. Он использует много оперативной памяти (в два раза размер входа), но это все равно меньше, чем ваш первоначальный подход. Это может быть быстрее, чем преобразование в строки на лета в компараторе, но он использует больше пиковых памяти. В зависимости от GC, это может сбивать свой путь через меньше общую память, хотя и требует меньше сбора.

Ответил 19/05/2009 в 16:24
источник пользователем

голоса
0

Один действительно Hacky метод (с использованием C) будет:

  • создать новый массив всех значений преобразуется в поплавки
  • делать вид, используя мантиссу (значащие) биты для сравнения

В Java (от сюда ):

long bits = Double.doubleToLongBits(5894.349580349);

boolean negative = (bits & 0x8000000000000000L) != 0; 
long exponent = bits & 0x7ff0000000000000L >> 52;
long mantissa = bits & 0x000fffffffffffffL;

так что вы отсортируете на долго mantissaздесь.

Ответил 19/05/2009 в 17:13
источник пользователем

голоса
1

Если все числа меньше, чем 1E + 18, вы могли бы бросить каждый номер в UINT64, умножить на десять и добавить одну, а затем умножить на десять, пока они не по крайней мере 1E + 19. Тогда сортировать их. Чтобы вернуть исходные номера, разделить каждую цифру на десять, пока последняя цифра не является ненулевым (он должен быть один), а затем разделить на десять раз больше.

Ответил 27/06/2012 в 15:41
источник пользователем

голоса
1

Вопрос не указывает, как лечить отрицательные числа в лексикографическом порядке сортировки. Методы на основе строки, представленные ранее, как правило, будут сортировать отрицательные значения к фронту; например, {-123, -345, 0, 234, 78} будет оставлен в таком порядке. Но если предполагается, что знаки минус были проигнорированы, порядок вывода должен быть {0, -123, 234, -345, 78}. Можно было бы адаптировать метод на основе строки для получения этого заказа на несколько-громоздких дополнительных испытаний.

Это может быть проще, как в теории, и код, чтобы использовать компаратор, который сравнивает дробные части логарифмов двух целых чисел. То есть, он будет сравнивать мантиссы основания 10 логарифмов двух чисел. Логарифм на основе компаратор будет работать быстрее или медленнее, чем струны на основе сравнения, в зависимости от спецификации производительности с плавающей точкой центральной процессора и по качеству реализации.

Код Java показан в конце этого ответа включает в себя два логарифмы на основе компараторов: alogCompareи slogCompare. Бывший игнорирует знаки, так что будет производить {0, -123, 234, -345, 78} из {-123, -345, 0, 234, 78}.

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

Раздел «DAR Rand» показывает массив случайных данных , darкак генерируются. Она читает по горизонтали и затем вниз, 5 элементов в каждой строке. Обратите внимание, что массивы sar, laraи larsизначально являются несортированными копиями dar.

Раздел «Дар сортировки» является darпосле сортировки с помощью Arrays.sort(dar);.

В разделе «сар закон» показывает массив sarпосле сортировки с Arrays.sort(sar,lexCompare);, где lexCompareпохож на Comparatorпоказанный в ответ Джейсон Коэна.

Раздел «лар с журнала» показывает массив larsпосле сортировки с помощью Arrays.sort(lars,slogCompare);, иллюстрирующая способ логарифм основе , что дает тот же порядок , как это делают lexCompareи другие методы на основе строки.

Раздел «LAR журнала» показывает массив laraпосле сортировки с помощью Arrays.sort(lara,alogCompare);, иллюстрирующим способом логарифма основой , который игнорирует минус знаки.

dar rand    -335768    115776     -9576    185484     81528
dar rand      79300         0      3128      4095    -69377
dar rand     -67584      9900    -50568   -162792     70992

dar sort    -335768   -162792    -69377    -67584    -50568
dar sort      -9576         0      3128      4095      9900
dar sort      70992     79300     81528    115776    185484

 sar lex    -162792   -335768    -50568    -67584    -69377
 sar lex      -9576         0    115776    185484      3128
 sar lex       4095     70992     79300     81528      9900

lar s log    -162792   -335768    -50568    -67584    -69377
lar s log      -9576         0    115776    185484      3128
lar s log       4095     70992     79300     81528      9900

lar a log          0    115776   -162792    185484      3128
lar a log    -335768      4095    -50568    -67584    -69377
lar a log      70992     79300     81528     -9576      9900

Java-код показан ниже.

// Code for "How can I sort numbers lexicographically?" - jw - 2 Jul 2014
import java.util.Random;
import java.util.Comparator;
import java.lang.Math;
import java.util.Arrays;
public class lex882954 {
// Comparator from Jason Cohen's answer
    public static Comparator<Integer> lexCompare = new Comparator<Integer>(){
        public int compare( Integer x, Integer y ) {
            return x.toString().compareTo( y.toString() );
        }
    };
// Comparator that uses "abs." logarithms of numbers instead of strings
    public static Comparator<Integer> alogCompare = new Comparator<Integer>(){
        public int compare( Integer x, Integer y ) {
            Double xl = (x==0)? 0 : Math.log10(Math.abs(x));
            Double yl = (y==0)? 0 : Math.log10(Math.abs(y));
            Double xf=xl-xl.intValue();
            return xf.compareTo(yl-yl.intValue());
        }
    };
// Comparator that uses "signed" logarithms of numbers instead of strings
    public static Comparator<Integer> slogCompare = new Comparator<Integer>(){
        public int compare( Integer x, Integer y ) {
            Double xl = (x==0)? 0 : Math.log10(Math.abs(x));
            Double yl = (y==0)? 0 : Math.log10(Math.abs(y));
            Double xf=xl-xl.intValue()+Integer.signum(x);
            return xf.compareTo(yl-yl.intValue()+Integer.signum(y));
        }
    };
// Print array before or after sorting
    public static void printArr(Integer[] ar, int asize, String aname) {
        int j;
        for(j=0; j < asize; ++j) {
            if (j%5==0)
                System.out.printf("%n%8s ", aname);
            System.out.printf(" %9d", ar[j]);
        }
        System.out.println();
    }
// Main Program -- to test comparators
    public static void main(String[] args) {
        int j, dasize=15, hir=99;
        Random rnd = new Random(12345);
        Integer[] dar = new Integer[dasize];
        Integer[] sar = new Integer[dasize];
        Integer[] lara = new Integer[dasize];
        Integer[] lars = new Integer[dasize];

        for(j=0; j < dasize; ++j) {
            lara[j] = lars[j] = sar[j] = dar[j] = rnd.nextInt(hir) * 
                rnd.nextInt(hir) * (rnd.nextInt(hir)-44);
        }
        printArr(dar, dasize, "dar rand");
        Arrays.sort(dar);
        printArr(dar, dasize, "dar sort");
        Arrays.sort(sar, lexCompare);
        printArr(sar, dasize, "sar lex");
        Arrays.sort(lars, slogCompare);
        printArr(lars, dasize, "lar s log");
        Arrays.sort(lara, alogCompare);
        printArr(lara, dasize, "lar a log");
    }
}
Ответил 02/07/2014 в 17:38
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more