Массив, размер которого увеличивается по мере продолжения цикла C

#arrays #pointers #dynamic #linked-list #malloc

#массивы #указатели #динамический #связанный список #malloc

Вопрос:

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

 #include <stdio.h>

int main () {

  int x = 0;
  int *F = malloc(sizeof(int)); //I want F to start out as :- 
  F[0] = 1;                     // 1 by 1
  F[1] = 2;                     // 1 by 2 such that it increases in size when assigned
  int now = 2;

  int evenSum = 2;

  while (x <= 40000) {


    F[now] = F[now-1]   F[now-2];
    x = F[now];

    if (F[now] % 2)
    {
        evenSum  = F[now];
    }

      now;
  }

  printf("The outcome is %dn", evenSum);

  //free(F);
  // Yes this is problem 2 of euler challenge, i already got a working static model

}
  

Заранее большое спасибо

РЕДАКТИРОВАТЬ То, что я на самом деле ищу, — это сумма всех четных значений fib до предельного значения 40M. Я мог бы (что я сделал в первый раз) суммировать четные числа по мере их появления во время последовательности fib. Это означало, что я не сохранил массив некоторого произвольного размера. Цель этого сообщения — создать растущую память, которая просто продолжает потреблять память, пока не дойдет до ответа. Ниже приведен код, который я получил из блестящего ответа, который был дан.

 #include <assert.h>
#include <stdlib.h>
#include <stdio.h>

struct vector {
    size_t size;
    int *data;
};

void vector_resize(struct vector *vector, size_t size) {
    if (vector->size >= size)
        return;
    while (vector->size < size)
        vector->size *= 2;
    vector->data = realloc(vector->data, sizeof(int) * vector->size);
    assert(vector->data != NULL);
}

struct vector * vector_init() {
    struct vector *vector = malloc(sizeof(*vector));
    vector->size = 4;
    vector->data = malloc(vector->size * sizeof(int));
    return vector;
}

void vector_free(struct vector *vector) {
    free(vector->data);
    free(vector);
}

void vector_put(struct vector *vector, size_t index, int data) {
    vector_resize(vector, index 1);
    vector->data[index] = data;;
}

int vector_get(struct vector *vector, size_t index) {
    vector_resize(vector, index 1);
    return vector->data[index];
}

int main() {
    struct vector *vector = vector_init();

    int fibNow = 0;

    int now = 2;
    vector_put(vector, 0, 1);
    vector_put(vector, 1, 2);

    int evenSum = 2;

    while (fibNow <= 4000000) {
        fibNow = vector_get(vector, (now-1))   vector_get(vector, (now-2));
        vector_put(vector, now, fibNow);

        if (fibNow % 2 == 0) {
            evenSum  = fibNow;
        }

          now;
    }
    printf("The outcome is %dn", evenSum);

    // vector_put(vector, 0, 5);
    // vector_put(vector, 9, 2);
    // int i;
    // for (i=0; i<10;   i)
    //     printf("index 0: %dn", vector_get(vector, i));
    vector_free(vector);
}
  

Комментарии:

1. Вы можете выделить массив в куче с malloc(sizeof(int) * NUMELEMENTS) помощью . Проверьте, realloc чтобы перераспределить достаточно места для вашего расширяющегося массива.

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

3. Мне нужен полный список номеров fib в памяти, чтобы я мог манипулировать ими позже. Четная сумма для чисел fib менее 40 КБ генерируется «на лету», и у меня нет желания хранить это.

4. что касается этого кода: int F = malloc(sizeof(int)); //Я хочу, чтобы F начинался как:- F[0] = 1; // 1 на 1 F[1] = 2; Поскольку код содержит только одну область для int, значениеf[1] повреждает кучу (из которой malloc выделил свою область. Следовательно, действие кода не определено в ‘F [1] = 2’, и любое действие после этого является лишь вопросом удачи. В качестве первоначального исправления предложите, чтобы в malloc было достаточно места как минимум для 3 int, т.Е. int *F = (int )malloc(sizeof(int)*3);

5. эта строка: now; приводит к строке: F[now] = F[now-1] F [now-2]; постепенно повреждая все больше и больше кучи.

Ответ №1:

Итак, в C нам не разрешено перегружать operator[] . Но мы все равно могли бы создать объект, который функционирует как ваш запрос:

 #include <assert.h>
#include <stdlib.h>
#include <stdio.h>

struct vector {
    size_t size;
    int *data;
};

void vector_resize(struct vector *vector, size_t size) {
    if (vector->size >= size)
        return;
    while (vector->size < size)
        vector->size *= 2;
    vector->data = realloc(vector->data, sizeof(int) * vector->size);
    assert(vector->data != NULL);
}

struct vector * vector_init() {
    struct vector *vector = malloc(sizeof(*vector));
    vector->size = 4;
    vector->data = malloc(vector->size * sizeof(int));
    return vector;
}

void vector_free(struct vector *vector) {
    free(vector->data);
    free(vector);
}

void vector_put(struct vector *vector, size_t index, int data) {
    vector_resize(vector, index 1);
    vector->data[index] = data;;
}

int vector_get(struct vector *vector, size_t index) {
    vector_resize(vector, index 1);
    return vector->data[index];
}

int main() {
    struct vector *vector = vector_init();

    vector_put(vector, 0, 5);
    vector_put(vector, 9, 2);

    for (int i=0; i<10;   i)
        printf("index 0: %dn", vector_get(vector, i));

    vector_free(vector);
}
  

Кроме того, интересно посмотреть на версию C о том, что это может быть. C делает это гораздо более похожим на ваш исходный код, потому что мы можем перегрузить operator[] для произвольных объектов.

 #include <cstdio>
#include <vector>

template <typename T>
class auto_growing_vector {
    private:
        std::vector<T> data;

    public:
        T amp; operator[](size_t index) {
            if (index >= data.size())
                data.resize(index   1);
            return data[index];
        }
};

int main() {
    auto_growing_vector<int> vector;

    vector[0] = 5;
    vector[9] = 2;

    for (int i=0; i<10;   i)
        printf("index 0: %dn", vector[i]);
}
  

Комментарии:

1. Зачем вам перегружать оператор [] для этого? В C разрешено использовать a[i], где ‘a’ — указатель произвольного типа на произвольную ячейку памяти, а ‘i’ — целое число, которое смещает местоположение для i*sizeof(тип указателя), так что a[i] <=> *(a i) Спасибо 🙂

2. @ETFovac: Извините, я не могу понять ваш вопрос. Обратите внимание, что в коде C vector_put() делает больше, чем просто return data[i] , и в коде C operator[]() также делает больше, чем просто return data[i] . Кроме того, обратите внимание, что auto_growing_vector это объект C , а не массив.

3. @ETFovac OP указал, что он хочет a[i] перераспределить a , чтобы это a[i] не был доступ за пределы

4. Я неправильно понял, что вы сказали. Теперь все в порядке. 🙂

5. Это именно то, что я хотел, также известный как связанные списки

Ответ №2:

В общем, realloc это должно сработать за вас. Пример (это всего лишь фрагмент — остальное вам нужно будет сделать самостоятельно):

 int *F;
F = malloc(2 * sizeof *F); // note you have to start with 2 elements for your code, not 1
F[0] = 1;
F[1] = 2;
// when you know you need to increase the size of F:
temp = realloc(F, n * sizeof *F); // where n is the new size in elements
if(temp != NULL) F = temp; // note that the block may have moved to a new place!
else {
  printf("unable to grow the array to %d elements!n", n);
  free(F);
  exit(0);
}
  

Конечно, для решения этой задачи вам не нужно сохранять все числа Фибоначчи — только последние два. На самом деле это предполагает гораздо более простой код. Позвольте мне начать, если для вас, и посмотреть, сможете ли вы его закончить (поскольку вы решаете задачи Эйлера, которые сводятся к тому, чтобы разобраться в этом самостоятельно):

 int first = 1;
int second = 1; // usually you start with 1,1,2,3,...
int temp, current;
int count;
int N = 4000; // where we stop

for(count = 2; count < N; count   ) {
  current = first   second;
  first = second;
  second = current;
}
  

Если вы присмотритесь, вы можете стать еще более эффективным, чем это (подсказка, вам действительно нужно сохранить только одно старое значение, а не два …)

Читая комментарии, если вы хотите, чтобы все числа были в памяти, вы должны просто выделить достаточно места с самого начала:

 F = malloc(4000 * sizeof *F); 
  

и никаких дальнейших манипуляций не требуется. Убедитесь, что в этом случае ваш последний индекс равен 3999 (поскольку массивы индексируются с нулевым индексом).

Комментарии:

1. У меня есть рабочая статическая модель, вся цель этого эксперимента заключалась в создании функции, которая просто продолжает работать до тех пор, пока не будет выполнен оператор while . Я полагаю, что в отношении эффективности, на что вы намекаете, это что-то вроде current = current previous; previous = current - previous;

2. Теперь я вижу, что вы ищете не n-е число Fib, а первое число, большее чего-то. На самом деле вы можете получить хорошее приближение размера числа Фибоначчи из F(n) ~ phi ^ n / sqrt(5), где phi = 1,618033… итак, чтобы получить число Фибоначчи больше N, вам нужно выделить около log(sqrt(5) * N) / log(phi) элементов. Добавьте дополнительные 10 для хорошей оценки, и у вас почти наверняка все получится.

3. Я не знал, что это математический взлом. Спасибо, что показали мне. То, что я на самом деле ищу, — это сумма всех четных значений fib до предельного значения 40 Тыс. Я мог бы (что я сделал в первый раз) суммировать четные числа по мере их появления во время последовательности fib. Это означало, что я не сохранил массив некоторого произвольного размера. Цель этого сообщения — создать растущую память, которая просто продолжает потреблять память, пока не дойдет до ответа.

Ответ №3:

Одним из способов было бы использовать 2D-массив int[n][n] с большим количеством неиспользуемого пространства

II Более простым способом было бы увеличивать размер массива на каждой итерации с помощью функции realocate . Только в этом случае либо:

a) каждый элемент исходного массива будет указателем на новый массив длиной i (i — номер итерации), затем вы должны изменить местоположение исходного массива, чтобы сделать размер для нового указателя, затем выделить i *sizeof(int) новой памяти для этого нового массива, на который будет указывать указатель.

б) Вы должны создать линеаризованную линейную матрицу, в которой исходный массив будет содержать только числа, а не указатели. На каждой итерации вы должны увеличивать его размер для новых элементов. Линеаризованная треугольная матрица — это одномерный массив чисел, в котором данные сохраняются следующим образом:

 ordinary matrix: (/ = wasted memory)
A/// 
BC//
DEF/
GHIJ

linarized triangular matrix
ABCDEFGHIJ
  

Вы можете получить доступ к линейному элементу треугольной матрицы E с координатами [y,x] = [2,1] (элемент ‘A’ берется за начало координат), например

 sum=0;
for(iy=0;iy<y;iy  )
  for(ix=0;ix<=y amp;amp; ix<x;ix  ) sum  ;
//myLinMatr[sum]=='E'