I'm trying to implement a simple game where the array symbolizes people standing in a circle, drawing an integer at the beginning of the game. each iteration is the size of the integer and removes people from the game.
so if array[]={a,b,c,d} and integer=3;
c is the first to be removed. Then b , then d. the winner is a.
at the end I need to print the array with the results by order of removal. so the printed array should be: a, d, b, c.
I'm trying to accomplish this only with use of pointers with no additional arrays.
Here's what i got, the problem is i'm trying to restart the for loop from the correct index and iterate through the remaining players who have not yet lost and cant get it right:
char *names[] = {"Tyrion Lannister","Daenerys Targaryen","Jon Snow","Arya Stark","Theon Greyjoy", "Joffrey Baratheon","Khal Drogo","Ted Mosby","Marshall Eriksen","Robin Scherbatsky","Barney Stinson", "Lily Aldrin", "Tracy McConnell", "Ted Mosby", "Leonard Hofstadter","Sheldon Cooper", "Penny", "Howard Wolowitz", "Raj Koothrappali", "Bernadette Rostenkowski-Wolowitz","Amy Farrah Fowler", "Gregory House", "Lisa Cuddy", "James Wilson","Eric Foreman", "Allison Cameron", "Robert Chase" ,"Lawrence Kutner", "Chris Taub","Remy 13 Hadley", "Amber Volakis"};
int Length = 31;
int number = 10;
char *tmp;
int i = 0, j;
int boom = number;
for (int i = number - 1; i < Length; i += boom - 1)
{
tmp = *(names + i);
for (int index = i; index < Length - 1; index++)
{
*(names + index) = *(names + index + 1);
}
*(names + Length - 1) = tmp;
Length--;
if (((number - 1) + i) >= Length)
{
int tempIndex = i;
i = tempIndex - Length;
printf("tmep index is %d, i is %d, Length is %d\n", tempIndex, i, Length);
}
}
for (i = 0; i < 31; i++)
printf("%s\n", names[i]);
I also tried another way with % operator, but couldn't quite get it done. Help would be much appreciated:
for (int i = number - 1; i < Length * (number - 1); i += boom - 1)
{
int t = i % Length;
printf("%d\n", t);
if (t < size)
{
counter++;
tmp = *(names + t);
// tmptwo = *(names + 31 - j);
for (int index = t; index < size - 1; index++)
{
*(names + index) = *(names + index + 1);
}
*(names + size - 1) = tmp;
size--;
printf("size %d\n", size);
}
}
You are thinking along the correct lines, but this is one circumstance where declaring and defining a simple function to shift elements down within an array moving the losing index to the end will simplify your life. By doing it this way, your only chores within the body of your code will be to provide the losing index and keeping track of the live number of players that remain with a simple counter.
An implementation of a function to move a given index to the last index for a given size could be similar to the following where a is the array to be reordered, elem_idx is the element index to move to the last element within sz elements:
void element_to_last (int *a, int elem_idx, int sz)
{
if (elem_idx > sz - 1) { /* valdate index in range */
fprintf (stderr, "error: index %d out of range for size %d\n",
elem_idx, sz);
return;
}
int i = elem_idx, /* declare, initialize i, tmp */
tmp = *(a + i);
if (elem_idx == sz - 1) /* elem_idx is last index */
return; /* no-swap */
for (; i < sz - 1; i++) /* loop shifting elements down */
*(a + i) = *(a + i + 1);
*(a + i) = tmp; /* set last to tmp */
}
(note: you will want to validate the element index to move to the end is within the valid range of indexes, and there is no need to perform the swap if the losing index is already the last in range).
A short working example where the WRAP constant simply controls output of no more than WRAP values per-line when printing results and the added DEBUG define allows outputting of additional information showing each operation if -DDEBUG is included in your compile string, e.g.
#include <stdio.h>
#ifndef WRAP
#define WRAP 10
#endif
void element_to_last (int *a, int elem_idx, int sz)
{
if (elem_idx > sz - 1) { /* valdate index in range */
fprintf (stderr, "error: index %d out of range for size %d\n",
elem_idx, sz);
return;
}
int i = elem_idx, /* declare, initialize i, tmp */
tmp = *(a + i);
if (elem_idx == sz - 1) { /* elem_idx is last index */
#ifdef DEBUG
fprintf (stderr, " index %d (%d) is last index %d - no swap.\n",
elem_idx, tmp, sz - 1);
#endif
return; /* no-swap */
}
#ifdef DEBUG
printf (" index %d (%d) to end %d\n", elem_idx, tmp, sz - 1);
#endif
for (; i < sz - 1; i++) /* loop shifting elements down */
*(a + i) = *(a + i + 1);
*(a + i) = tmp; /* set last to tmp */
}
void prn_array (int *a, int sz, int wrap)
{
for (int i = 0; i < sz; i++) {
if (i && i % wrap == 0)
putchar ('\n');
printf (" %2d", *(a + i));
}
putchar ('\n');
}
int main (void) {
int a[] = {0,1,2,3,4,5,6,7,8,9}, /* original array order */
sz = sizeof a/sizeof *a, /* nelem in original */
n = sz, /* n tracks remaining size */
loser[] = {2,0,7,3,2,3,2,1,1}, /* order of losing indexes */
lsz = sizeof loser/sizeof *loser; /* nelem in loser array */
puts ("before:");
prn_array (a, sz, WRAP);
puts ("\nelimination\n(remove indexes 2,0,7,3,2,3,2,1,1):");
for (int i = 0; i < lsz; i++) {
element_to_last (a, loser[i], n > 0 ? n-- : n);
prn_array (a, sz, WRAP);
}
puts ("\nafter:");
prn_array (a, sz, WRAP);
}
(note: the remaining players, e.g. the remaining number of elements is tracked with n while sz preserves the original size of the full array. lsz is used for the size of the loser array)
Example Use/Output
Without DEBUG defined, the output simply shows the state of the array after a loser is moved to the end of the remaining players:
$ ./bin/array_rotate
before:
0 1 2 3 4 5 6 7 8 9
elimination
(remove indexes 2,0,7,3,2,3,2,1,1):
0 1 3 4 5 6 7 8 9 2
1 3 4 5 6 7 8 9 0 2
1 3 4 5 6 7 8 9 0 2
1 3 4 6 7 8 5 9 0 2
1 3 6 7 8 4 5 9 0 2
1 3 6 8 7 4 5 9 0 2
1 3 8 6 7 4 5 9 0 2
1 8 3 6 7 4 5 9 0 2
1 8 3 6 7 4 5 9 0 2
after:
1 8 3 6 7 4 5 9 0 2
DEBUG Output
With DEBUG defined, additional information showing each index and (value) being moved to the end index shown, along with a note whether then end index was the loser in which case "no-swap" was attempted:
$ ./bin/array_rotate
before:
0 1 2 3 4 5 6 7 8 9
elimination
(remove indexes 2,0,7,3,2,3,2,1,1):
index 2 (2) to end 9
0 1 3 4 5 6 7 8 9 2
index 0 (0) to end 8
1 3 4 5 6 7 8 9 0 2
index 7 (9) is last index 7 - no swap.
1 3 4 5 6 7 8 9 0 2
index 3 (5) to end 6
1 3 4 6 7 8 5 9 0 2
index 2 (4) to end 5
1 3 6 7 8 4 5 9 0 2
index 3 (7) to end 4
1 3 6 8 7 4 5 9 0 2
index 2 (6) to end 3
1 3 8 6 7 4 5 9 0 2
index 1 (3) to end 2
1 8 3 6 7 4 5 9 0 2
index 1 (8) is last index 1 - no swap.
1 8 3 6 7 4 5 9 0 2
after:
1 8 3 6 7 4 5 9 0 2
Look things over and let me know if you have further questions.
This code should solve the problem by using modulo.
int size = 31;
int inc = 10;
for (int i = 0; i < size; i++)
{
int t = ((i + 1) * inc) % (size - i);
int *tmp = *(names + t);
printf("%d\n", t);
for (int j = t; j < (size - 1); j++)
{
*(names + j) = *(names + j + 1);
}
*(names + size - 1) = tmp;
}
Related
I want to count sequence of numbers together, by always adding the next number to the sum of numbers before. Then do it all again but start one number up. Like this. Then find duplicated sums.
1 5 2 4 2 2 2(sequence)
0..1: 1 5 sum=6
0..2: 1 5 2 sum=8
0..3: 1 5 2 4 sum=12
0..4: 1 5 2 4 2 sum=14
0..5: 1 5 2 4 2 2 sum=16
0..6: 1 5 2 4 2 2 2 sum=18
1..2: 5 2 sum=7
1..3: 5 2 4 sum=11
1..4: 5 2 4 2 sum=13
1..5: 5 2 4 2 2 sum=15
1..6: 5 2 4 2 2 2 sum=17
2..3: 2 4 sum=6
2..4: 2 4 2 sum=8
2..5: 2 4 2 2 sum=10
2..6: 2 4 2 2 2 sum=12
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int count = 0;
char temp;
int sekvence[10000];
int countedsequence[10000];
int duplication = 0;
//save user input
do
{
scanf("%d%c", &sekvence[count], &temp);
count++;
} while (temp != '\n');
sekvence[count];
//somehow count it and save to countedsequence
countedsequence[0] = sekvence[0];
countedsequence[0] = countedsequence[0] + sekvence[0 + 1];
for (int i = 1; i < count - 1; i++)
{
countedsequence[i] = countedsequence[i - 1] + sekvence[i + 1];
}
//find duplicated numbers in countedsequence
for (int i = 0; i < count - 1; i++)
{
for (int j = i + 1; j < count - 1; j++)
{
if (countedsequence[i] == countedsequence[j])
{
duplication++;
break;
}
}
}
//idk some printing for testing
for (int i = 0; i < count - 1; i++)
{
printf("%d ", countedsequence[i]);
}
printf("%d\n", duplication);
return 0;
}
I only managed to count from start to end how do I start counting again with one up to the end?
I usually revise op's code into an solution but the use of non-English variable mean that require unnecessary extra effort. The only working functionality is the interactive input handling but that gets in way during development.
To generate each range of the sequence using two loops (for start and end) and at third to generate the sum of said range (sequence_sum()).
With an unordered array of numbers we can find duplicates by a tweaked partition algorithm that that swap elements into 3 sections: one instance of each duplicates [0; i[, todo [i; sum_len - i[ and processed [ sum_len; [. This is an O(n^2) algorithm. A more efficient O(n) solution would use a hash map from sum to count at the cost of additional code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
size_t duplicate_sum(size_t sum_len, const int sum[sum_len], int duplicate[sum_len]) {
size_t i = 0;
memcpy(duplicate, sum, sum_len * sizeof(*sum));
for(; i < sum_len;) {
int found = 0;
for(size_t j = i + 1; j < sum_len; j++) {
if(duplicate[i] == duplicate[j]) {
found = 1;
swap(duplicate + j, duplicate + sum_len - 1);
sum_len--;
}
}
if(found) {
i++;
} else {
swap(duplicate + i, duplicate + sum_len - 1);
sum_len--;
}
}
return i;
}
void sequence_sum(size_t len, const int sequence[len], int sum[len * (len - 1) / 2]) {
for(size_t i = 0, s = 0; i < len - 1; i++) {
for(size_t j = i + 1; j < len; j++, s++) {
sum[s] = 0;
printf("%zu..%zu: ", i, j);
for(size_t k = i; k <= j; k++) {
printf(" %d", sequence[k]);
sum[s] += sequence[k];
}
printf("%*.0ssum=%d\n", (int) (2 * len - 2 * (j - i)), "", sum[s]);
}
}
}
int main(void) {
int sequence[] = { 1,5,2,4,2,2,2 };
size_t sequence_len = sizeof sequence / sizeof *sequence;
size_t sum_len = sequence_len * (sequence_len - 1) / 2;
int sum[sum_len];
sequence_sum(sequence_len, sequence, sum);
int duplicate[sum_len];
printf("duplicates: ");
size_t duplicate_len = duplicate_sum(sum_len, sum, duplicate);
for(size_t i = 0; i < duplicate_len; i++) {
printf(" %d", duplicate[i]);
}
printf("\n");
and the output:
0..1: 1 5 sum=6
0..2: 1 5 2 sum=8
0..3: 1 5 2 4 sum=12
0..4: 1 5 2 4 2 sum=14
0..5: 1 5 2 4 2 2 sum=16
0..6: 1 5 2 4 2 2 2 sum=18
1..2: 5 2 sum=7
1..3: 5 2 4 sum=11
1..4: 5 2 4 2 sum=13
1..5: 5 2 4 2 2 sum=15
1..6: 5 2 4 2 2 2 sum=17
2..3: 2 4 sum=6
2..4: 2 4 2 sum=8
2..5: 2 4 2 2 sum=10
2..6: 2 4 2 2 2 sum=12
3..4: 4 2 sum=6
3..5: 4 2 2 sum=8
3..6: 4 2 2 2 sum=10
4..5: 2 2 sum=4
4..6: 2 2 2 sum=6
5..6: 2 2 sum=4
duplicates: 6 8 12 10 4
I have the following task:
Give a recursive algorithm for filling an array like this: one 1, two 2, tree 3, four 4, ... ,n n.
For example, with n = 4 the array is supposed to look like:
{ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 }
My attempt is
Function filling(T, k, n)
if (n = 0) do return 1
else if (0 <= k) do filling (T, n - k + 1, n);
else filling(T, k, n - 1);
fi
fi
with filling(T, k, n)
k: block start case of number n,
n: number,
T: array
As you correctly show, the function needs an array T, a starting index k of the area to fill, and a size of the task n, i.e. a maximum number for filling.
The algorithm can be described like this:
if n is zero, return;
otherwise:
perform filling from 1 till n-1,
then append n items of value n
The question arises, how should the last line know where it should start adding.
One simple answer is: the routine may return the index one past the filled area:
size_t filling(int T[], size_t k, size_t n)
{
if (n != 0)
{
k = filling(T, k, n - 1);
for (size_t i = 0; i < n; i++)
T[k + i] = n;
}
return k + n;
}
Make sure your array is at least n*(n+1)/2 items long, so that you don't overrun it.
You need to take care of two things here:
Number of digits to be insert in the array starting from 1, and
Count of a digit (which should be equal to digit itself) when inserting it in the array.
Since, the algorithm should be implemented using recursion, we should know the terminating condition of recursion.
Algorithm:
Insert digit in array.
Check if the count of digit inserted in array is equal to digit or not:
If count == digit then reset the count to 0 and increase digit by 1.
If count < digit go to step 1.
If digit to be inserted in array is greater than n, terminate the recursion.
Implementation in c++:
#include <iostream>
#include <vector>
void filling (std::vector <int>& arr, int k, int n) {
static int c;
if (c >= n) {
c = 0;
return;
}
if (k <= c) {
k = k + 1;
arr.push_back(c + 1);
} else {
k = 0;
c = c + 1;
}
filling (arr, k, n);
}
int main() {
std::vector <int> arr;
int n;
std::cout << "Enter a number:" << std::endl;
std::cin >> n;
filling (arr, 0, n);
for (const auto& x : arr) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
Output:
# ./a.out
Enter a number:
5
1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
# ./a.out
Enter a number:
10
1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 10 10 10 10 10 10 10 10 10 10
# ./a.out
Enter a number:
1
1
# ./a.out
Enter a number:
4
1 2 2 3 3 3 4 4 4 4
I'm trying to make a program that shifts all the elements of an array to the right by one and move the last element into the first position. My problem is when I run my code it's giving me the number 5 twice. Can someone help me, maybe my logic or my for loop is not right?
#include <stdio.h>
int main ()
{
int array[6];
int x;
int temp;
printf("Enter six numbers.\n\n");
for (x = 0; x < 6; x++) {
printf("Enter a number : ", x + 1);
scanf("%d", &array[x]);
temp = array[x - 1];
}
for (x = 6 - 1; x > 0; x--) {
array[x] = array[x - 1];
}
array[0] = temp;
for (x = 0; x < 6; x++) {
printf("%d\n", array[x]);
}
return 0;
}
You could make a for loop like
for(i=0; i<SIZE; ++i)
{
scanf("%d", &arr[(i+1)%SIZE]);
}
The (i+1)%SIZE would evaluate to i+1 if i+1 is less than SIZE. Otherwise, it would wrap around.
Or you can
int t=arr[SIZE-1];
for(i=SIZE-1; i>0; --i)
{
arr[i]=arr[i-1];
}
arr[0]=t;
save the last element into a temporary variable, shift other elements towards right and finally assign the first element the value in the temporary variable.
As Gourav pointed out, in the first iteration of your first for loop, arr[x-1] would become arr[-1] as x is 0. You are trying to access memory which is not part of that array. This invokes undefined behavior.
I will try to explain it with the very easiest algorithm and yes easy means not efficient as from performance perspective.
For example, let's say you have an array of six elements:
1 2 3 4 5 6
From the question, all I understood is that you want to reverse this array to have a final array to be:
6 5 4 3 2 1
A naive way of doing is to store the first element in a temporary variable, assign the second element to the first element and after that assign the temporary variable to the second element and repeat this until all elements are swapped as shown below:
temp = arr[0]
arr[0] = arr[1]
arr[1] = temp
You will need two loops to do this, one decrementing and one incrementing
1 2 3 4 5 6 i=0; j=5
2 1 3 4 5 6 i=1; j=5
2 3 1 4 5 6 i=2; j=5
2 3 4 1 5 6 i=3; j=5
2 3 4 5 1 6 i=4; j=5
2 3 4 5 6 1 i=5; j=5
Next, you have to execute the above loop with one less iteration:
2 3 4 5 6 1 i=0; j=4
3 2 4 5 6 1 i=1; j=4
3 4 2 5 6 1 i=2; j=4
3 4 5 2 6 1 i=3; j=4
3 4 5 6 2 1 i=4; j=4
So, the loops would be:
for(i=5; i>0; i--)
{
for(j=0; j<i; j++)
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
You can make things a bit more generic by adding a direction to your shift routine and putting the shift code in a function that takes the array, the number of members and the direction to shift as arguments. Then its just a matter of iterating in the proper direction and shifting the elements in the correct direction and putting the final value in the proper place. For example, you could write a simple function as follows:
/** shift array index in in circular manner by 1 in the
* 0 - left or 1 - right direction.
*/
void arrayshift (int *a, size_t nmemb, int dir)
{
if (dir) { /* shift to RIGHT */
int tmp = a[nmemb - 1];
for (size_t i = nmemb - 1; i > 0; i--)
a[i] = a[i - 1];
a[0] = tmp;
}
else { /* shift to LEFT */
int tmp = a[0];
for (size_t i = 1; i < nmemb; i++)
a[i - 1] = a[i];
a[nmemb - 1] = tmp;
}
}
A simple test routine could be:
#include <stdio.h>
enum { LEFT, RIGHT };
void arrayshift (int *a, size_t nmemb, int dir);
int main (int argc, char **argv) {
int a[] = { 1, 2, 3, 4, 5, 6 },
dir = argc > 1 ? LEFT : RIGHT;
size_t n = sizeof a / sizeof *a;
printf ("original:");
for (size_t i = 0; i < n; i++)
printf (" %d", a[i]);
putchar ('\n');
arrayshift (a, n, dir);
printf ("shifted :");
for (size_t i = 0; i < n; i++)
printf (" %d", a[i]);
putchar ('\n');
return 0;
}
/** shift array index in in circular manner by 1 in the
* 0 - left or 1 - right direction.
*/
void arrayshift (int *a, size_t nmemb, int dir)
{
if (dir) { /* shift to RIGHT */
int tmp = a[nmemb - 1];
for (size_t i = nmemb - 1; i > 0; i--)
a[i] = a[i - 1];
a[0] = tmp;
}
else { /* shift to LEFT */
int tmp = a[0];
for (size_t i = 1; i < nmemb; i++)
a[i - 1] = a[i];
a[nmemb - 1] = tmp;
}
}
Example Use/Output
$ ./bin/array_cir_shift_by_1
original: 1 2 3 4 5 6
shifted : 6 1 2 3 4 5
$ ./bin/array_cir_shift_by_1 0
original: 1 2 3 4 5 6
shifted : 2 3 4 5 6 1
You can also pass the number of element to shift the array by and use the modulo operator to help with the indexing. (that is left for another day)
I tried to sort arr by excluding those who were already selected as the largest numbers but it didn't work.
The result is this:
As I intended, at first cycle, the store is {9, 0, 0, 0, 0 ... } and when arr[i] becomes 9, the rest of process should be skipped. I have to sort it without additional functions and it's too difficult to me. What is the problem?
int i = 0;
int j = 0;
int num = 0;
int sign = 0;
int arr[10] = { 1,5,3,4,8,7,5,9,8,0 };
int max = arr[0];
int store[10] = { 0 };
int k = 0;
for (j = 0; j < 10; j++) {
printf("store: ");
for (int n = 0; n < 10; on++)
printf("%d ", store[n]);
printf("\n");
for (i = 0; i < 10; i++) {
sign = 0;
k = 0;
while (k < 10) {
if (arr[i] == store[k]) {
sign = 1;
break;
}
k++;
}
if (sign == 1) {
continue;
}
if (arr[i] > max) {
max = arr[i];
}
}
store[j] = max;
}
You have several errors here:
The array store has a size of 10, but in the jth pass through the outer loop, only j values have been filled in; the rest is still zero. So whenever you iterate over store, you should use j as upper limit.
You are looking for the max in each iteration. Therefore, it is not enough to initialise max once outside the outer loop. You do that, and it will stay 9 ever after. You should reset max for every j.
Finally, your idea to go through the array to see whether you have already processed a certain value does not work. Your array has duplicates, two 8's and two 5's. You will only place one eight and one five with your strategy and re-use the last value of max for the last two elements. (Plus, that idea lead to O(n³) code, which is very wasteful.
You can work around that by keeping an extra array where you store whether (1) or not (0) you have already processed a value at a certain index or by setting processed entries in the array to a very low value.
What you want to implement is selection sort: Find the maximum value in the whole list and move it to the front. Then find the maximum in the whole list except the first item and move it to the second slot and so on:
* 1 5 3 4 8 7 5 9 8 0
9 * 5 3 4 8 7 5 1 8 0
9 8 * 3 4 5 7 5 1 8 0
9 8 8 * 4 5 7 5 1 3 0
9 8 8 7 * 5 4 5 1 3 0
9 8 8 7 5 * 4 5 1 3 0
9 8 8 7 5 5 * 4 1 3 0
9 8 8 7 5 5 4 * 1 3 0
9 8 8 7 5 5 4 3 * 1 0
9 8 8 7 5 5 4 3 1 * 0
9 8 8 7 5 5 4 3 1 0 *
Here, all items to the left of the asterisk have been sorted and everything to the right of the asterisk is still unsorted. When the * (at position j) has moved to the right, the whole array is sorted.
This sort is in-place: It destroys the original order of the array. That is useful, because the position of an element tells us whether it has been processed or not. In the third iteration, the algorithm can distinguish between the 8 that has been sorted and the 8 that hasn't been sorted yet. (This sort is often described as sorting a hand of cards: Look fo the lowest, put it to the left and so on. If you must sort into a second array, copy the original array and sort the copy in place.)
Here's the code that sorts your array and prints out the diagram above:
#include <stdlib.h>
#include <stdio.h>
int main()
{
int arr[10] = {1, 5, 3, 4, 8, 7, 5, 9, 8, 0};
int i = 0;
int j = 0;
for (j = 0; j < 10; j++) {
int imax = j;
int swap = arr[j];
// print array
for (i = 0; i < 10; i++) {
if (i == j) printf("* ");
printf("%d ", arr[i]);
}
printf("\n");
// find index of maximum item
for (i = j + 1; i < 10; i++) {
if (arr[i] > arr[imax]) {
imax = i;
}
}
// swap first unsorted item and maximum item
arr[j] = arr[imax];
arr[imax] = swap;
}
// print fully sorted array
for (i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
printf("*\n");
return 0;
}
Use i and j.
N is 10 and the data consists of shuffled numbers 0 to N-1.
j goes from 0 to N-1. At each step, you want to fill it with
the maximum of the unprocessed input.
So i goes from j+1 to N-1, in the inner loop. If arr[j] < arr[i],
swap arr[i] and arr[j].
It speeds up considerably as you get towards the end.
In the hottest part of my program (90% of time according to gprof), I need to sum one array A into another B. Both arrays are 2^n (n is 18..24) sized and holds an integer (for simplicity, actually the element stored is mpz_t or small int array). The rule of summing: for each i in 0..2^n-1, set B[i] = sum (A[j]), where j is bit vector, and j & ~ i == 0 (in other words, k-th bit of any j can't be set to 1 if k-th bit of i is not 1).
My current code (this is a body of innermost loop) does this in the time of 2^(1.5 * n) sums, because I will iterate for each i on (in average) 2^(n/2) elements of A.
int A[1<<n]; // have some data
int B[1<<n]; // empty
for (int i = 0; i < (1<<n); i++ ) {
/* Iterate over subsets */
for (int j = i; ; j=(j-1) & i ) {
B[i] += A[j]; /* it is an `sum`, actually it can be a mpz_add here */
if(j==0) break;
}
}
My I mentioned, that almost any sum is recomputed from the values, summed earlier. I suggest, there can be code, doing the same task in the time of n* 2^n sums.
My first idea is that B[i] = B[i_without_the_most_significant_bit] + A[j_new]; where j_new is only j's having the most_significant bit from i in '1' state. This halves my time, but this is not enough (still hours and days on real problem size):
int A[1<<n];
int B[1<<n];
B[0] = A[0]; // the i==0 will not work with my idea and clz()
for (int i = 1; i < (1<<n); i++ ) {
int msb_of_i = 1<< ((sizeof(int)*8)-__builtin_clz(i)-1);
int i_wo_msb = i & ~ msb;
B[i] = B[i_wo_msb];
/* Iterate over subsets */
for (int j_new = i; ; j_new=(j_new-1) & i ) {
B[i] += A[j_new];
if(j_new==msb) break; // stop, when we will try to unset msb
}
}
Can you suggest better algorithm?
Additional image, list of i and j summed for each i for n=4:
i j`s summed
0 0
1 0 1
2 0 2
3 0 1 2 3
4 0 4
5 0 1 4 5
6 0 2 4 6
7 0 1 2 3 4 5 6 7
8 0 8
9 0 1 8 9
a 0 2 8 a
b 0 1 2 3 8 9 a b
c 0 4 8 c
d 0 1 4 5 8 9 c d
e 0 2 4 6 8 a c e
f 0 1 2 3 4 5 6 7 8 9 a b c d e f
Note the similarity of figures
PS the msb magic is from here: Unset the most significant bit in a word (int32) [C]
Divide and conquer anyone? Now not in-place.
void sums(int *a, int n, int *b) {
if (n <= 0) {
*b = *a;
return;
}
int m = 1 << (n - 1);
sums(a, n - 1, b);
sums(a + m, n - 1, b + m);
for (int i = 0; i < m; i++) {
b[m + i] += b[i];
}
}