Radix Sort Base 16 (Hexadecimals) - c

I have spent more 10hr+ on trying to sort the following(hexadecimals) in LSD radix sort, but no avail. There is very little material on this subject on web.
0 4c7f cd80 41fc 782c 8b74 7eb1 9a03 aa01 73f1
I know I have to mask and perform bitwise operations to process each hex digit (4 bits), but have no idea on how and where.
I'm using the code (I understand) from GeeksforGeeks
void rsort(int a[], int n) {
int max = getMax(a, n);
for (int exp = 1; max / exp > 0; exp *= 10) {
ccsort(a, n, exp);
}
}
int getMax(int a[], int n) {
int max = a[0];
int i = 0;
for (i = 0; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
void ccsort(int a[], int n, int exp) {
int count[n];
int output[n];
int i = 0;
for (i = 0; i < n; i++) {
count[i] = 0;
output[i] = 0;
}
for (i = 0; i < n; i++) {
++count[(a[i] / exp) % 10];
}
for (i = 1; i <= n; i++) {
count[i] += count[i - 1];
}
for (i = n - 1; i >= 0; i--) {
output[count[(a[i] / exp) % 10] - 1] = a[i];
--count[(a[i] / exp) % 10];
}
for (i = 0; i < n; i++) {
a[i] = output[i];
}
}
I have also checked all of StackOverFlow on this matter, but none of them covers the details.

Your implementation of radix sort is slightly incorrect:
it cannot handle negative numbers
the array count[] in function ccsort() should have a size of 10 instead of n. If n is smaller than 10, the function does not work.
the loop for cumulating counts goes one step too far: for (i = 1; i <= n; i++). Once again the <= operator causes a bug.
you say you sort by hex digits but the code uses decimal digits.
Here is a (slightly) improved version with explanations:
void ccsort(int a[], int n, int exp) {
int count[10] = { 0 };
int output[n];
int i, last;
for (i = 0; i < n; i++) {
// compute the number of entries with any given digit at level exp
++count[(a[i] / exp) % 10];
}
for (i = last = 0; i < 10; i++) {
// update the counts to have the index of the place to dispatch the next
// number with a given digit at level exp
last += count[i];
count[i] = last - count[i];
}
for (i = 0; i < n; i++) {
// dispatch entries at the right index for its digit at level exp
output[count[(a[i] / exp) % 10]++] = a[i];
}
for (i = 0; i < n; i++) {
// copy entries batch to original array
a[i] = output[i];
}
}
int getMax(int a[], int n) {
// find the largest number in the array
int max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
void rsort(int a[], int n) {
int max = getMax(a, n);
// for all digits required to express the maximum value
for (int exp = 1; max / exp > 0; exp *= 10) {
// sort the array on one digit at a time
ccsort(a, n, exp);
}
}
The above version is quite inefficient because of all the divisions and modulo operations. Performing on hex digits can be done with shifts and masks:
void ccsort16(int a[], int n, int shift) {
int count[16] = { 0 };
int output[n];
int i, last;
for (i = 0; i < n; i++) {
++count[(a[i] >> shift) & 15];
}
for (i = last = 0; i < 16; i++) {
last += count[i];
count[i] = last - count[i];
}
for (i = 0; i < n; i++) {
output[count[(a[i] >> shift) & 15]++] = a[i];
}
for (i = 0; i < n; i++) {
a[i] = output[i];
}
}
void rsort16(int a[], int n) {
int max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
for (int shift = 0; (max >> shift) > 0; shift += 4) {
ccsort16(a, n, shift);
}
}
It would be approximately twice as fast to sort one byte at a time with a count array of 256 entries. It would also be faster to compute the counts for all digits in one pass, as shown in rcgldr's answer.
Note that this implementation still cannot handle negative numbers.

There's a simpler way to implement a radix sort. After checking for max, find the lowest power of 16 >= max value. This can be done with max >>= 4 in a loop, incrementing x so that when max goes to zero, then 16 to the power x is >= the original max value. For example a max of 0xffff would need 4 radix sort passes, while a max of 0xffffffff would take 8 radix sort passes.
If the range of values is most likely to take the full range available for an integer, there's no need to bother determining max value, just base the radix sort on integer size.
The example code you have shows a radix sort that scans an array backwards due to the way the counts are converted into indices. This can be avoided by using an alternate method to convert counts into indices. Here is an example of a base 256 radix sort for 32 bit unsigned integers. It uses a matrix of counts / indices so that all 4 rows of counts are generated with just one read pass of the array, followed by 4 radix sort passes (so the sorted data ends up back in the original array). std::swap is a C++ function to swap the pointers, for a C program, this can be replaced by swapping the pointers inline. t = a; a = b; b = t, where t is of type uint32_t * (ptr to unsigned 32 bit integer). For a base 16 radix sort, the matrix size would be [8][16].
// a is input array, b is working array
uint32_t * RadixSort(uint32_t * a, uint32_t *b, size_t count)
{
size_t mIndex[4][256] = {0}; // count / index matrix
size_t i,j,m,n;
uint32_t u;
for(i = 0; i < count; i++){ // generate histograms
u = a[i];
for(j = 0; j < 4; j++){
mIndex[j][(size_t)(u & 0xff)]++;
u >>= 8;
}
}
for(j = 0; j < 4; j++){ // convert to indices
m = 0;
for(i = 0; i < 256; i++){
n = mIndex[j][i];
mIndex[j][i] = m;
m += n;
}
}
for(j = 0; j < 4; j++){ // radix sort
for(i = 0; i < count; i++){ // sort by current lsb
u = a[i];
m = (size_t)(u>>(j<<3))&0xff;
b[mIndex[j][m]++] = u;
}
std::swap(a, b); // swap ptrs
}
return(a);
}

void int_radix_sort(void) {
int group; //because extracting 8 bits
int buckets = 1 << 8; //using size 256
int map[buckets];
int mask = buckets - 1;
int i;
int cnt[buckets];
int flag = NULL;
int partition;
int *src, *dst;
for (group = 0; group < 32; group += 8) {
// group = 8, number of bits we want per round, we want 4 rounds
// cnt
for (int i = 0; i < buckets; i++) {
cnt[i] = 0;
}
for (int j = 0; j < n; j++) {
i = (lst[j] >> group) & mask;
cnt[i]++;
tmp[j] = lst[j];
}
//map
map[0] = 0;
for (int i = 1; i < buckets; i++) {
map[i] = map[i - 1] + cnt[i - 1];
}
//move
for (int j = 0; j < n; j++) {
i = (tmp[j] >> group) & mask;
lst[map[i]] = tmp[j];
map[i]++;
}
}
}
After hours of researching I came across the answer. I'm still do not understand what is going on in this code/answer. I cannot get my head wrapped around the concept. Hopefully, someone can explain.

I see your points. I think negative numbers are easy to sort after the list has been sorted with something like loop, flag, and swap. wb unsigned float points? – itproxti Nov 1 '16 at 16:02
As for handling floating points there might be a way, for example 345.768 is the number, it needs to be converted to an integer, i.e. make it 345768, I multiplied 1000 with it. Just like the offset moves the -ve numbers to +ve domain, so will multiplying by 1000, 10000 etc will turn the floats to numbers with their decimal part as all zeros. Then they can be typecasted to int or long. However with large values, the whole reformed number may not be accomodated within the entire int or long range.
The number that is to be multiplied has to be constant, just like the offset so that the relationship among the magnitudes is preserved. Its better to use powers of 2 such as 8 or 16, as then bitshifting operator can be used. However just like the calculation of offset takes some time, so will calculation of the multiplier will take some time. The whole array is to be searched to calculate the least number that when multiplied will turn all the numbers with zeros in decimal parts.
This may not compute fast but still can do the job if required.

Related

Sorting integers by sum of their digits

I'm trying to write a program that will sort an array of 20 random numbers by the sums of their digits.
For example:
"5 > 11" because 5 > 1+1 (5 > 2).
I managed to sort the sums but is it possible to return to the original numbers or do it other way?
#include <stdio.h>
void sortujTab(int tab[], int size){
int sum,i;
for(int i=0;i<size;i++)
{
while(tab[i]>0){//sum as added digits of an integer
int p=tab[i]%10;
sum=sum+p;
tab[i]/=10;
}
tab[i]=sum;
sum=0;
}
for(int i=0;i<size;i++)//print of unsorted sums
{
printf("%d,",tab[i]);
}
printf("\n");
for(int i=0;i<size;i++)//sorting sums
for(int j=i+1;j<=size;j++)
{
if(tab[i]>tab[j]){
int temp=tab[j];
tab[j]=tab[i];
tab[i]=temp;
}
}
for(int i=0;i<20;i++)//print of sorted sums
{
printf("%d,",tab[i]);
}
}
int main()
{
int tab[20];
int size=sizeof(tab)/sizeof(*tab);
for(int i=0;i<=20;i++)
{
tab[i]=rand()%1000;// assamble the value
}
for(int i=0;i<20;i++)
{
printf("%d,",tab[i]);//print unsorted
}
printf("\n");
sortujTab(tab,size);
return 0;
}
There are two basic approach :
Create a function that return the sum for an integer, say sum(int a), then call it on comparison, so instead of tab[i] > tab [j] it becomes sum(tab[i]) > sum (tab[j])
Store the sum into a different array, compare with the new array, and on swapping, swap both the original and the new array
The first solution works well enough if the array is small and takes no extra memory, while the second solution didn't need to repeatedly calculate the sum. A caching approach is also possible with map but it's only worth it if there are enough identical numbers in the array.
Since your numbers are non-negative and less than 1000, you can encode the sum of the digits in the numbers itself. So, this formula will be true: encoded_number = original_number + 1000 * sum_of_the_digits. encoded_number/1000 will decode the sum of the digits, and encoded_number%1000 will decode the original number. Follow the modified code below. The numbers enclosed by parentheses in the output are original numbers. I've tried to modify minimally your code.
#include <stdio.h>
#include <stdlib.h>
void sortujTab(int tab[], int size)
{
for (int i = 0; i < size; i++) {
int sum = 0, n = tab[i];
while (n > 0) { //sum as added digits of an integer
int p = n % 10;
sum = sum + p;
n /= 10;
}
tab[i] += sum * 1000;
}
for (int i = 0; i < size; i++) { //print of unsorted sums
printf("%d%c", tab[i] / 1000, i < size - 1 ? ',' : '\n');
}
for (int i = 0; i < size; i++) { //sorting sums
for (int j = i + 1; j < size; j++) {
if (tab[i] / 1000 > tab[j] / 1000) {
int temp = tab[j];
tab[j] = tab[i];
tab[i] = temp;
}
}
}
for (int i = 0; i < size; i++) { //print of sorted sums
printf("%d(%d)%c", tab[i] / 1000, tab[i] % 1000, i < size - 1 ? ',' : '\n');
}
}
int main(void)
{
int tab[20];
int size = sizeof(tab) / sizeof(*tab);
for (int i = 0; i < size; i++) {
tab[i] = rand() % 1000; // assamble the value
}
for (int i = 0; i < size; i++) {
printf("%d%c", tab[i], i < size - 1 ? ',' : '\n'); //print unsorted
}
sortujTab(tab, size);
return 0;
}
If the range of numbers doesn't allow such an encoding, then you can declare a structure with two integer elements (one for the original number and one for the sum of its digits), allocate an array for size elements of this structure, and initialize and sort the array using the digit sums as the keys.
You can sort an array of indexes rather than the array with data.
#include <stdio.h>
//poor man's interpretation of sumofdigits() :-)
int sod(int n) {
switch (n) {
default: return 0;
case 5: return 5;
case 11: return 2;
case 1000: return 1;
case 9: return 9;
}
}
void sortbyindex(int *data, int *ndx, int size) {
//setup default indexes
for (int k = 0; k < size; k++) ndx[k] = k;
//sort the indexes
for (int lo = 0; lo < size; lo++) {
for (int hi = lo + 1; hi < size; hi++) {
if (sod(data[ndx[lo]]) > sod(data[ndx[hi]])) {
//swap indexes
int tmp = ndx[lo];
ndx[lo] = ndx[hi];
ndx[hi] = tmp;
}
}
}
}
int main(void) {
int data[4] = {5, 11, 1000, 9};
int ndx[sizeof data / sizeof *data];
sortbyindex(data, ndx, 4);
for (int k = 0; k < sizeof data / sizeof *data; k++) {
printf("%d\n", data[ndx[k]]);
}
return 0;
}

Radix sort for non-negative integers (bitwise)

I am trying to learn how to work with bitwise shifts and sorting algorithms, and I am lost. I want to use the binary representation of integers and using bitwise operations to sort the integers using radix sort. I already applied a radix sort using decimal in C (code below), but I am not sure how to do apply the same logic to a radix sort using bitwise operations. I think I would use the mask of 0xFF to left shift the binary representation of the integers.
How would I go about applying a radix sort to integer values using a left shift and a bitwise AND?
#include <stdlib.h>
#include <stdio.h>
int max(int arr[], int size){
int max = arr[0];
for(int i = 1;i<size;i++){
if(arr[i]>max){
max = arr[i];
}
}
return max;
}
void radixsort(int arr[], int size) {
int maxNum = max(arr, size);
int tenExp;
for (tenExp = 1; maxNum / tenExp > 0; tenExp *= 10){
int returnArr[size];
int i, numOccurences[10] = { 0 };
for (i = 0; i < size; i++)
numOccurences[(arr[i] / tenExp) % 10]++;
for (i = 1; i < 10; i++)
numOccurences[i] += numOccurences[i - 1];
for (i = size - 1; i >= 0; i--) {
returnArr[numOccurences[(arr[i] / tenExp) % 10] - 1] = arr[i];
numOccurences[(arr[i] / tenExp) % 10]--;
}
for (i = 0; i < size; i++)
arr[i] = returnArr[i];
}
}
void main(int argc, char *argv[]){
int size = 0;
int* array = malloc(0 * sizeof(int));
int res;
int temp;
int count;
int i = 1;
printf("Enter a count: ");
scanf("%d",&count);
while(i <= count){
printf("Enter number: \n");
res = scanf("%d",&temp);
if(res == EOF) break;
size++;
array = realloc(array,size*sizeof(int));
array[size-1] = temp;
i++;
}
radixsort(array,size);
for(int i=0;i<size;i++){
printf("%d\n",array[i]);
}
}
I will advise you to modify the same code to do a radix-sort for binary numbers only using arithmetic operations. This should be simple. You have to perform operation using 2 instead of 10.
Once you do the above changes, you can use following equivalent bitwise operation.
y = x>>1 is equivalent to y = x/2
y = x<<1 is equivalent to y = x*2
y = x&1 is equivalent to y = x%2

Adding two numbers [1, 10^10000] as arrays of chars - C

I tackled the problem by first figuring out the length of two given numbers and aligning the one with less digits (if one exists) into a new array so that the ones, tens, hundreds etc. align with the bigger number's ones, tens, hundreds, etc.
Then I wanted to save the sum of each two aligned elements (with a mod of 10) into a new array while checking if the sum of digits is greater than 10 - just the basic sum stuff. Now the problem occurs with adding two elements into the aplusb integer and I've tried fixing it with writing
int aplusb = (lengthA[max-i]-'0') +(temp[max-i]-'0');
but it doesn't work. I'm stuck and I don't know what to do. Please help.
The whole code:
#include <stdio.h>
#include <math.h>
int main(){
char a[10000];
char b[10000];
scanf("%s %s", &a, &b);
char sum[10000];
int lengthA = 0;
int lengthB = 0;
int i = 0;
while(a[i]){
i++;
} lengthA = i;
i = 0;
while(b[i]){
i++;
} lengthB = i;
char temp[10000];
int aplusb;
int carry = 0;
int max = lengthA;
int difference = abs(lengthA - lengthB);
if(lengthA>lengthB){
for(i=0; i<lengthA; i++){
temp[i+difference]=b[i];
}
for(i=0; i<=max; i++){
aplusb = lengthA[max-i]+temp[max-i]; //<-- this is the problematic line
if(carry = 1) aplusb++;
if(aplusb>9){
carry = 1;
aplusb%=10;
}
sum[i]=aplusb;
}
}
for(i=0; i<=max; i++){
printf("%c", sum[i]);
}
/*
if(lengthB>lengthA){
max = lengthB;
for(i=0; i<lengthB; i++){
temp[i+difference]=a[i];
}
}*/
return 0;
}
Doing operations and storing on very large numbers is very akin to doing operations and storing polynomials, i.e. with x = 10. a0 + a1.10 + a2.10^2 ... + an.10^n.
There are many polynomial libraries on the Internet, where you could find inspiration. All operations on your very large numbers can be expressed in terms of polynomials. This means that by using base 2^8, or even base 2^63, instead of base 10 to internally store your large numbers you would greatly improve performance.
You must also normalize your coefficients after operations to keep them positive. Operations may result in a negative coefficient, That can easily be fixed, as it is very similar to borrowing after a subtraction, this means coefficients must be larger than your base by 1bit.
To convert back to base 10, you'd need to solve r (your result) for v (your value), such as r(10)=v(2^63). This has only one solution, if you enforce the positive coefficients rule.
[note] After thinking about it some more: the rule on positive coefficients may only be necessary for printing, after all.
Example: adding. no memory error checking
int addPolys(signed char** result, int na, const signed char* a, int nb, const signed char* b)
{
int i, nr, nmin, carry, *r;
nr = max(na, nb) + 1;
nmin = min(na, nb);
r = malloc(sizeof(signed char) * (na + nb + 1));
if (nb < na)
{
nr = nb;
}
for (i = 0; i < nmin; ++i)
{
r[i] = a[i] + b[i];
}
for (; i < na; ++i)
{
r[i] = a[i];
}
for (; i < nb; ++i)
{
r[i] = b[i];
}
r[nr - 1] = 0;
// carry - should really be a proc of its own, unoptimized
carry = 0;
for (i = 0; i < nr; ++i)
{
r[i] += carry;
if (r[i] > 10)
{
carry = r[i] / 10;
r[i] %= 10;
}
else if (r[i] < 0)
{
carry = (r[i] / 10) - 1;
r[i] -= (carry * 10);
}
else
carry = 0;
}
// 'remove' leading zeroes
for (i = nr - 1; i > 0; --i)
{
if (r[i] != 0) break;
}
++i;
*result = r;
if (i != nr)
{
*result = realloc(i * sizeof(signed char));
}
return i; // return number of digits (0 being 1 digit long)
}
That code is working now for any two positive numbers with up to ten thousand digits:
#include <stdio.h>
#include <math.h>
#include <string.h>
int main(){
char chara[10000];
char charb[10000];
scanf("%s %s", &chara, &charb);
int lengthA = strlen(chara);
int lengthB = strlen(charb);
int max = lengthA;
if(lengthB>lengthA) max=lengthB;
int dif = abs(lengthA - lengthB);
//ustvari int tabele
int a[max];
int b[max];
int sum[max+1];
// nastavi nule
int i;
for(i=0; i<max; i++){
a[i] = 0;
b[i] = 0;
sum[i] = 0;
} sum[max] = 0;
//prekopiraj stevila iz char v int tabele &obrni vrstni red
for(i=0; i<lengthA; i++){
a[i] = chara[lengthA-i-1]-'0';
}
for(i=0; i<lengthB; i++){
b[i] = charb[lengthB-i-1]-'0';
}
int vsota;
int prenos = 0;
for(i=0; i<max; i++){
vsota = a[i]+b[i] + prenos;
if(vsota>=10) prenos = 1;
else if (vsota<10) prenos = 0;
sum[i]=vsota%10;
}
if(prenos==1){
sum[max] = 1;
for(i = max; i>=0; i--){
printf("%d", sum[i]);
}
} else {
for(i = max-1; i>=0; i--){
printf("%d", sum[i]);
}
}
return 0;
}

Calculating large power of a number in c

I am writing a program in c to store 2^100000, and I am using arrays to store the result.
Here is the full code:
#include <stdio.h>
#include <math.h>
int main()
{
int test, n, i, j, x, resul;
int a[200], m, temp;
scanf("%d", &test);
for (i = 0; i < test; i++) {
a[0] = 3; // initializes array with only 1 digit, the digit 1.
m = 1; // initializes digit counter
scanf("%d", &n);
temp = 0; // Initializes carry variable to 0.
for (i = 1; i < n; i++) {
for (j = 0; j < m; j++) {
x = a[j] * 2 + temp; //x contains the digit by digit product
a[j] = x % 10; //Contains the digit to store in position j
temp = x / 10; //Contains the carry value that will be stored on later indexes
}
while (temp > 0) { //while loop that will store the carry value on array.
a[m] = temp % 10;
temp = temp / 10;
m++; // increments digit counter
}
}
for (i = m - 1; i >= 0; i--) //printing answer
printf("%d", a[i]);
}
return 0;
}
Can some one tell me a more efficient way to do so to reduce the time complexity?
2^n in binary is an (n+1)-digit integer with every bit set to 0 except the most significant bit being set to 1. e.g: 32 = 2^5 = 0b100000
Likewise, 2^100000 can be computed by setting the 100001-th bit in a zeroed 100001 bit long integer to 1. O(1) is as time efficient as you can go.
There are several problems with your code:
The array a is defined with a size of only 200 digits. This is much too small for 2^100000 that has 30103 digits. You should increase the array size and check for overflow in the multiplication algorithm.
You initialize a[0] = 3; and comment this as the digit 1. Indeed you should write a[0] = 1;.
The second loop for (i = 1; i < n; i++) should include the desired power number: you should write for (i = 1; i <= n; i++).
You use the same loop variable for the outer loop and the second level ones, causing incorrect behavior.
You do not test the return value of scanf, causing undefined behavior on invalid input.
You do not check for overflow, invoking undefined behavior on large values.
Here is a corrected version:
#include <stdio.h>
int main()
{
int n, i, j, x, m, test, temp;
int a[32000];
if (scanf("%d", &test) != 1)
return 1;
while (test-- > 0) {
if (scanf("%d", &n) != 1)
break;
a[0] = 1; // initializes array with only 1 digit, the number 1.
m = 1; // initializes digit counter
temp = 0; // Initializes carry variable to 0.
for (i = 1; i <= n; i++) {
for (j = 0; j < m; j++) {
x = a[j] * 2 + temp; //x contains the digit by digit product
a[j] = x % 10; //Contains the digit to store in position j
temp = x / 10; //Contains the carry value that will be stored on later indexes
}
// while loop that will store the carry value on array.
if (temp > 0) {
if (m >= (int)(sizeof(a)/sizeof(*a)))
break;
a[m++] = temp;
temp = 0;
}
}
if (temp > 0) {
printf("overflow");
} else {
for (i = m - 1; i >= 0; i--) //printing answer
putchar('0' + a[i]);
}
printf("\n");
}
return 0;
}
Running this code with input 1 and 100000 on my laptop takes about 6,5 seconds. That's indeed quite inefficient. Using a few optimization techniques that do not really change the complexity of this simple iterative algorithm still can yield a dramatic performance boost, possibly 100 times faster.
Here are some ideas:
store 9 digits per int in the array instead of just 1.
multiply by 2^29 in each iteration instead of just 2, using long long to compute the intermediary result. Initialize the first step to 1 << (n % 29) to account for n not being a multiple of 29. 2^29 is the largest power of 2 less than 10^9.
Here is version that implements these two ideas:
#include <stdio.h>
int main() {
int n, i, j, m, test, temp;
int a[32000];
if (scanf("%d", &test) != 1)
return 1;
while (test-- > 0) {
if (scanf("%d", &n) != 1)
break;
i = n % 29;
n /= 29;
a[0] = 1 << i;
m = 1;
temp = 0;
for (i = 1; i <= n; i++) {
for (j = 0; j < m; j++) {
long long x = a[j] * (1LL << 29) + temp;
a[j] = x % 1000000000;
temp = x / 1000000000;
}
if (temp > 0) {
if (m >= (int)(sizeof(a)/sizeof(*a)))
break;
a[m++] = temp;
temp = 0;
}
}
if (temp > 0) {
printf("overflow");
} else {
printf("%d", a[m - 1]);
for (i = m - 2; i >= 0; i--)
printf("%09d", a[i]);
}
printf("\n");
}
return 0;
}
Running it on the same laptop computes the correct result in only 33ms, that's 200 times faster.
The Time Complexity is the same, but implementation is much more efficient.
Be aware that native C integers are limited, in practice to some power of two related to the word size of your computer (e.g. typically 32 or 64 bits). Read about <stdint.h> and int32_t & int64_t.
Maybe you want some bignums (or bigints), a.k.a. arbitrary precision arithmetic.
The underlying algorithms are very clever (and more efficient than the naive ones you learned in school). So don't try to reinvent them, and use a library like GMPlib

Sum of differences in array

Is there a more efficient way to achieve this:
Given an array A of size n and two positive integers a and b, find the sum floor(abs(A[i]-A[j])*a/b) taken over all pairs (i, j) where 0 <= i < j < n.
int A[n];
int a, b; // assigned some positive integer values
...
int total = 0;
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n; j++) {
total += abs(A[i]-A[j])*a/b; // want integer division here
}
}
To optimize this a little bit, I sorted the array (O(nlogn)) and then didn't use an abs function. Also, I cached the value a[i] before the inner for loop, so I could just read stuff from A sequentially. I was considering precomputing a/b and storing that in a float, but the extra casting just makes it slower (especially since I want to take the floor of the result).
I couldn't come up with a solution that was better than O(n^2).
Yes, there is a more efficient algorithm. It can be done it in O(n*log n). I don't expect there to be an asymptotically faster way, but I'm far from any idea of a proof.
Algorithm
First sort the array in O(n*log n) time.
Now, let us look at the terms
floor((A[j]-A[i])*a/b) = floor ((A[j]*a - A[i]*a)/b)
for 0 <= i < j < n. For each 0 <= k < n, write A[k]*a = q[k]*b + r[k] with 0 <= r[k] < b.
For A[k] >= 0, we have q[k] = (A[k]*a)/b and r[k] = (A[k]*a)%b with integer division, for A[k] < 0, we have q[k] = (A[k]*a)/b - 1 and r[k] = b + (A[k]*a)%b unless b divides A[k]*a, in which case we have q[k] = (A[k]*a)/b and r[k] = 0.
Now we rewrite the terms:
floor((A[j]*a - A[i]*a)/b) = floor(q[j] - q[i] + (r[j] - r[i])/b)
= q[j] - q[i] + floor((r[j] - r[i])/b)
Each q[k] appears k times with positive sign (for i = 0, 1, .. , k-1) and n-1-k times with negative sign (for j = k+1, k+2, ..., n-1), so its total contribution to the sum is
(k - (n-1-k))*q[k] = (2*k+1-n)*q[k]
The remainders still have to be accounted for. Now, since 0 <= r[k] < b, we have
-b < r[j] - r[i] < b
and floor((r[j]-r[i])/b) is 0 when r[j] >= r[i] and -1 when r[j] < r[i]. So
n-1
∑ floor((A[j]-A[i])*a/b) = ∑ (2*k+1-n)*q[k] - inversions(r)
i<j k=0
where an inversion is a pair (i,j) of indices with 0 <= i < j < n and r[j] < r[i].
Calculating the q[k] and r[k] and summing the (2*k+1-n)*q[k] is done in O(n) time.
It remains to efficiently count the inversions of the r[k] array.
For each index 0 <= k < n, let c(k) be the number of i < k such that r[k] < r[i], i.e. the number of inversions in which k appears as the larger index.
Then obviously the number of inversions is ∑ c(k).
On the other hand, c(k) is the number of elements that are moved behind r[k] in a stable sort (stability is important here).
Counting these moves, and hence the inversions of an array is easy to do while merge-sorting it.
Thus the inversions can be counted in O(n*log n) too, giving an overall complexity of O(n*log n).
Code
A sample implementation with a simple unscientific benchmark (but the difference between the naive quadratic algorithm and the above is so large that an unscientific benchmark is conclusive enough).
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
long long mergesort(int *arr, unsigned elems);
long long merge(int *arr, unsigned elems, int *scratch);
long long nosort(int *arr, unsigned elems, long long a, long long b);
long long withsort(int *arr, unsigned elems, long long a, long long b);
int main(int argc, char *argv[]) {
unsigned count = (argc > 1) ? strtoul(argv[1],NULL,0) : 1000;
srand(time(NULL)+count);
long long a, b;
b = 1000 + 9000.0*rand()/(RAND_MAX+1.0);
a = b/3 + (b-b/3)*1.0*rand()/(RAND_MAX + 1.0);
int *arr1, *arr2;
arr1 = malloc(count*sizeof *arr1);
arr2 = malloc(count*sizeof *arr2);
if (!arr1 || !arr2) {
fprintf(stderr,"Allocation failed\n");
exit(EXIT_FAILURE);
}
unsigned i;
for(i = 0; i < count; ++i) {
arr1[i] = 20000.0*rand()/(RAND_MAX + 1.0) - 2000;
}
for(i = 0; i < count; ++i) {
arr2[i] = arr1[i];
}
long long res1, res2;
double start = clock();
res1 = nosort(arr1,count,a,b);
double stop = clock();
printf("Naive: %lld in %.3fs\n",res1,(stop-start)/CLOCKS_PER_SEC);
start = clock();
res2 = withsort(arr2,count,a,b);
stop = clock();
printf("Sorting: %lld in %.3fs\n",res2,(stop-start)/CLOCKS_PER_SEC);
return EXIT_SUCCESS;
}
long long nosort(int *arr, unsigned elems, long long a, long long b) {
long long total = 0;
unsigned i, j;
long long m;
for(i = 0; i < elems-1; ++i) {
m = arr[i];
for(j = i+1; j < elems; ++j) {
long long d = (arr[j] < m) ? (m-arr[j]) : (arr[j]-m);
total += (d*a)/b;
}
}
return total;
}
long long withsort(int *arr, unsigned elems, long long a, long long b) {
long long total = 0;
unsigned i;
mergesort(arr,elems);
for(i = 0; i < elems; ++i) {
long long q, r;
q = (arr[i]*a)/b;
r = (arr[i]*a)%b;
if (r < 0) {
r += b;
q -= 1;
}
total += (2*i+1LL-elems)*q;
arr[i] = (int)r;
}
total -= mergesort(arr,elems);
return total;
}
long long mergesort(int *arr, unsigned elems) {
if (elems < 2) return 0;
int *scratch = malloc((elems + 1)/2*sizeof *scratch);
if (!scratch) {
fprintf(stderr,"Alloc failure\n");
exit(EXIT_FAILURE);
}
return merge(arr, elems, scratch);
}
long long merge(int *arr, unsigned elems, int *scratch) {
if (elems < 2) return 0;
unsigned left = (elems + 1)/2, right = elems-left, i, j, k;
long long inversions = 0;
inversions += merge(arr, left, scratch);
inversions += merge(arr+left,right,scratch);
if (arr[left] < arr[left-1]) {
for(i = 0; i < left; ++i) {
scratch[i] = arr[i];
}
i = 0; j = 0; k = 0;
int *lptr = scratch, *rptr = arr+left;
while(i < left && j < right) {
if (rptr[j] < lptr[i]) {
arr[k++] = rptr[j++];
inversions += (left-i);
} else {
arr[k++] = lptr[i++];
}
}
while(i < left) arr[k++] = lptr[i++];
}
return inversions;
}

Resources