powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / Мера неупорядоченности линейного массива и некоторые вопросы
25 сообщений из 53, страница 1 из 3
Мера неупорядоченности линейного массива и некоторые вопросы
    #39076248
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Здравствуйте.
Раздел "Различные структуры данных. Реализация" предназначен скорее для теоретических вопросов в контексте С++. Потому я решил не продолжать в нём дискуссию по данному конкретному вопросу. Спасибо Дмитрию и другим кто высказал своё мнение ранее.

Постановка задачи 1.

Для любого линейного непрерывного индексного массива T a[], размером n введём норму/меру неупорядоченности/measure по следующему правилу
, где . Необходимо разработать алгоритм с асимптотической сложностью.
Приведу пример: Пусть A задано следующим множеством чисел А = { 17, 5, 18, 1, 3, 4, 7, 11, 85, 8, 9 }. Тогда его мера равна:
.

Постановка задачи 2.
Пусть в постановке 1 исходный индексный массив содержит в себе только уникальные элементы.


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

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
#include <stdio.h>
#include <stdlib.h>

struct node
{
	int value;//key
	int counter;//number of elements in the tree over then current at time of adding
	int counter_r;//number of elements right subtree
	node *left, *right;
	void print();
};

void node::print()
{
	printf("%i %i %i   \n", value, counter_r, counter);
}

class tree
{
	node* root;
	int measure;
	void insert(node*& n, int v, int c);
	void print(node* n);
	void measure_calculate(node* n);
public:
	tree();
	tree(int* a, size_t n);
	void print();
	void measure_calculate();
	int measure_get();
};

tree::tree()
{
	root = NULL;
	measure = 0;
}

tree::tree(int* a, size_t n)
{
	root = NULL;
	measure = 0;
	for (int i = 0; i < n; ++i){
		this->insert(root, a[i], 0);
	}
}

void tree::insert(node*& n, int v, int c)
{
	node* cur;
	if (n == NULL){
		cur = (node*)malloc(sizeof(node)* 1);
		cur->value = v;
		cur->left = NULL;
		cur->right = NULL;
		cur->counter_r = 0;
		cur->counter = c;
		n = cur;
	}
	else{
		if (n->value > v){
			this->insert(n->left, v, c + n->counter_r + 1);
		}
		else{
			n->counter_r += 1;
			this->insert(n->right, v, c);
		}
	}
}

void tree::print()
{
	print(root);
}

void tree::print(node* n)
{
	if (n){
		print(n->left);
		n->print();
		print(n->right);
	}
}


void tree::measure_calculate()
{
	measure_calculate(root);
}

void tree::measure_calculate(node* n)
{
	if (n){
		measure_calculate(n->left);
		measure += n->counter;
		measure_calculate(n->right);
	}
}

int tree::measure_get()
{
	return measure;
}


int main()
{
	int b[11] = { 17, 5, 18, 1, 3, 4, 7, 11, 85, 8, 9 };
	tree t(b, 11);
	t.measure_calculate();
	printf("%i\n", t.measure_get());
	return 0;
}



1. measure лучше выделить в отдельный класс
2. Перегруженный деструктор(если можно так сказать) почему-то не работает так как должен работать. Потому я его убрал пока
3. и др

Подскажите пожалуйста в чём главные ошибки по реализации программы в оос. И может быть у кого-то есть ещё соображения по реализации алгоритма (с линейной скоростью или с лучшей организацией дерева за исключением вопросов балансировки(на данный момент))

PS
Я ещё не прочитал статью о различиях между malloc и new потому пока использую malloc, вы в своих программах на С++ используете только new ?
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39076254
RWolf
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryЯ ещё не прочитал статью о различиях между malloc и new потому пока использую malloc, вы в своих программах на С++ используете только new ?
malloc выделяет неинициализированный кусок памяти, new создаёт объект.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39076285
mcureenab
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
RWolfSashaMercuryЯ ещё не прочитал статью о различиях между malloc и new потому пока использую malloc, вы в своих программах на С++ используете только new ?
malloc выделяет неинициализированный кусок памяти, new создаёт объект.

Дефолтный new это вызов p = malloc... и вызов конструктора для p == this. В случае new [N], выделяется память под N объектов и для каждого вызывается конструктор.
Дефолтный delete, вызывает деструктор, потом free. В случае delete [], вызывается деструктор для каждого элемента массива, потом free для всего массива.

Нужно заметить, что в процессе конструирования и разрушения объекта происходит инициализация не только атрибутов, но и таблицы виртуальных методов (если они применяются). Прямого доступа к таблицам виртуальных методов у программиста нет.

Можно создать собственные new и delete и использовать в них иной менеджер памяти, отличный от стандартной кучи.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39076333
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryЯ ещё не прочитал статью о различиях между malloc и new потому пока использую malloc, вы в своих программах на С++ используете только new ?

Для создания объектов динамически -- new.
Для выделения просто памяти -- можно и malloc.

new в С++ выделяет память и инициализирует в этой памяти объект с помощью конструктора.
malloc про объекты вообще ничего не знает.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39076403
wst
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eсть еще вариант обойтись и без new и без malloc в своем коде, на стандартных контейнерах, make_unique и make_shared.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39076527
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Саша. Что такое СУПЕР ЛИНЕЙНАЯ функция?
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39076730
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сделал по этому алгоритму 18266267
Работает побыстрее перебора. На 100 тыс. элементов перебор 6.2 сек. этот 0.04 сек
Вот замеры
ЭлементовВремя1 млн.0.52 сек.2 млн.1.4 сек.3 млн.2.5 сек.4 млн.3.78 сек.8 млн.10.4 сек.
Попробовал сложность оценить, если сравнить с O(n * log2(n)) то вроде как даже чуть быстрее.
Например 8*log(8) = 24, у меня 10.4 / 0.52 = 20.

Исходник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

// Замер времени
class timer {
	clock_t start;
	char buf[20];
public:
	timer();
	void init(); // начало замера
	const char* value(); // текущее значение
};

timer::timer()
{
	init();
}

void timer::init()
{
	start = clock();
}

// Вывод времени
const char* timer::value()
{
	clock_t t = clock() - start;
	int sec = t / CLOCKS_PER_SEC;
	if(sec > 1000) {
		sprintf(buf, "%.0f sec", (double)t / CLOCKS_PER_SEC);
	} else if(sec > 100) {
		sprintf(buf, "%.1f sec", (double)t / CLOCKS_PER_SEC);
	} else if(sec > 10) {
		sprintf(buf, "%.2f sec", (double)t / CLOCKS_PER_SEC);
	} else {
		sprintf(buf, "%.3f sec", (double)t / CLOCKS_PER_SEC);
	}
	return buf;
}

//******************************************************
// Размер тестового массива
#define TEST_SIZE 10000
//******************************************************

// Заполнение исходных данных
void init(int* arr)
{
	timer t;
	// Заполняем уникальными значениями
	for(int i = 0; i != TEST_SIZE; i++) arr[i] = i;
	// Перемешиваем
	int r = 54323; // ГСЧ с постоянной последовательстью
	for(int i = 0; i != TEST_SIZE; i++) {
		do {
			r = (r * 12347 + 1) % TEST_SIZE;
		} while(r < 0);
		
		int j = arr[i];
		arr[i] = arr[r];
		arr[r] = j;
	}
	printf("Init %u elements. %s\n", TEST_SIZE, t.value());
}

// Вывод содержимого массива
void print(const char* text, int* arr)
{
	printf(text);
	unsigned int check = 0;
	for(int i = 0; i < TEST_SIZE; i++) {
		if(i < 10) printf("%d ", arr[i]);
		check += i * arr[i];
	}
	printf(" sum %X\n", check);
}

// Решение перебором
void perebor(int* data, int* res)
{
	timer t;
	for(int i = 0; i < TEST_SIZE; i++) {
		for(int j = 0; j < i; j++) {
			if(data[j] > data[i]) res[i]++;
		}
	}
	printf("Perebor %s\n", t.value());
}


int compare_int(const void* a, const void* b)
{
  return ( **(int**)a - **(int**)b );
}

// С сортировкой и массивом счетчиков
void with_counter(int* data, int* res)
{
	timer t;
	int** idx = (int**)malloc(TEST_SIZE * sizeof(int*)); // индексы сортировки
	if(!idx) {
		printf("No memory\n");
		return;
	}
	// Заполнение индексов и сортировка
	for(int i = 0; i < TEST_SIZE; i++) idx[i] = &data[i];
	qsort(idx, TEST_SIZE, sizeof(int*), compare_int);

	// Выбор шага счетчика
	int step = 2; 
	int step_bit = 1; 
	int y = TEST_SIZE;
	while(true) {
		int x = step / 2 + TEST_SIZE / (step * 2);
		if(x > y || step >= TEST_SIZE) {
			step >>= 1;
			step_bit--;
			break;
		}
		y = x;
		step <<= 1;
		step_bit++;
	}
	// Массив счетчиков
	int cnt_size = TEST_SIZE / step + 1;
	int* cnt = (int*) malloc(cnt_size * sizeof(int)); // счетчики
	if(!cnt) {
		printf("No memory\n");
		free(idx);
		return;
	}
	for(int i = 0; i < cnt_size; i++) cnt[i] = step;
	// Расчет
	int min = *idx[0];
	int mask = 0x7FFFFFFF ^ (step - 1); // маска для поиска первого элемента группы
	for(int i = 0; i < TEST_SIZE; i++) {
		int pos = idx[i] - data; // индекс минимального элемента в data
		 // обнуление посчитанного
		data[pos] = min;
		int cnt_pos = pos >> step_bit; // индекс счетчика
		cnt[cnt_pos]--;
		// Подсчет от начала группы
		for(int j = pos & mask; j < pos; j++) {
			if(data[j] > data[pos]) res[pos]++;
		}
		// Добавление полных групп
		for(int j = 0; j < cnt_pos; j++) {
			res[pos] += cnt[j];
		}
	}
	free(cnt);
	free(idx);
	printf("Counters %s\n", t.value());
}


int main(int argc, char* argv[])
{

	int* data = (int*)calloc(TEST_SIZE, sizeof(int)); // массив исходных данных
	int* res = (int*)calloc(TEST_SIZE, sizeof(int)); // массив для результата
	if(!data || !res) {
		printf("No memory\n");
	} else {
		// Заполнение исходных данных
		init(data); 
		print("Data   ", data);
		// Расчет перебором
		perebor(data, res);
		print("Result ", res);
		memset(res, 0, sizeof(int)*TEST_SIZE);

		// Расчет c сортировкой и групповыми счетчиками
		with_counter(data, res);
		// Вывод результата
		print("Result ", res);
	}

	if(data) free(data);
	if(res) free(res);

	system("pause");
	return 0;
}

...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077070
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Может быть я не так перевожу, но в англоязычной литературе встречается термин superlinear function. Скорость роста данной функции выше чем у линейной функции, например, асимптотика quick sort принадлежит классу superlinear function, и составляет . Так и тут
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077081
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спасибо, мне кажется он работает. Почему вывод времени реализован так как он реализован ?
Можно ли упростить реализацию with_counter() не меняя алгоритм ?
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077090
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryПочему вывод времени реализован так как он реализован ?
Что конкретно интересует?
Максимально упростил использование. Для замера достаточно создать объект, а в конце получить его состояние.
SashaMercuryМожно ли упростить реализацию with_counter() не меняя алгоритм ?
Думаю что нет. Разве что в начале подбор шага переделать на формулу. Там идет поиск минимума step / 2 + TEST_SIZE / (step * 2)
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077091
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сделать вывод временив одном формате независимо от того какое у нас время(1 секунда или 10)
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077092
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Просто не понял почему вывод времени сделан именно так. Можно было вывести в одном формате
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077100
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryПросто не понял почему вывод времени сделан именно так. Можно было вывести в одном формате
Для читабельности. Чтобы лишнего не было. Время надо для сравнительной оценки. Точности в 0,1% вполне достаточно, т.е. надо 3 первых десятичных знака. Поэтому зачем писать лишнее если можно не писать.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077106
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ДмитрийДумаю что нет. Разве что в начале подбор шага переделать на формулу. Там идет поиск минимума step / 2 + TEST_SIZE / (step * 2)

Этим минимумом будут либо значения на границах, либо в точке. Если я правильно понял. Разве не так ?
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077115
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вроде так, только надо еще округлить до ближайшей степени двойки. Я специально так сделал, чтобы исключить деление и остатки от деления.
т.е. для 1 млн должно получиться 1024, для 100 млн. - 8192.

Можно попробовать точное значение брать, делений не так уж и много будет. Поправлю попозже.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077164
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
поправил
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
// С сортировкой и массивом счетчиков
void with_counter(int* data, int* res)
{
	timer t;
	int** idx = (int**)malloc(TEST_SIZE * sizeof(int*)); // индексы сортировки
	if(!idx) {
		printf("No memory\n");
		return;
	}
	// Заполнение индексов и сортировка
	for(int i = 0; i < TEST_SIZE; i++) idx[i] = &data[i];
	qsort(idx, TEST_SIZE, sizeof(int*), compare_int);

	// Выбор шага счетчика
	int step = (int)sqrt((double)TEST_SIZE);
	// Массив счетчиков
	int cnt_size = TEST_SIZE / step + 1;
	int* cnt = (int*) malloc(cnt_size * sizeof(int)); // счетчики
	if(!cnt) {
		printf("No memory\n");
		free(idx);
		return;
	}
	for(int i = 0; i < cnt_size; i++) cnt[i] = step;
	// Расчет
	int min = *idx[0];
	for(int i = 0; i < TEST_SIZE; i++) {
		int pos = idx[i] - data; // индекс минимального элемента в data
		 // обнуление посчитанного
		data[pos] = min;
		int cnt_pos = pos / step; // индекс счетчика
		cnt[cnt_pos]--;
		// Подсчет от начала группы
		for(int j = cnt_pos * step; j < pos; j++) {
			if(data[j] > data[pos]) res[pos]++;
		}
		// Добавление полных групп
		for(int j = 0; j < cnt_pos; j++) {
			res[pos] += cnt[j];
		}
	}
	free(cnt);
	free(idx);
	printf("Counters %s\n", t.value());
}


Время точно такое же как тут 18278951 Но кода меньше и он читабельнее.

PS Я так и не вспомнил как вывести что минимум это корень от TEST_SIZE. Математика забыта напрочь
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077174
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спасибо, теперь у меня есть с чем сравнивать свой алгоритм :)
PS


И теперь нужно подставить в исходную функцию, и получим значение функции
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077175
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Больше мы её(реализацию) никак не сократим/оптимизируем ?
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077184
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryМожет быть я не так перевожу, но в англоязычной литературе встречается термин superlinear function. Скорость роста данной функции выше чем у линейной функции, например, асимптотика quick sort принадлежит классу superlinear function, и составляет . Так и тут
Возможно ее так назвали из-за внешней схожести с линейной.

...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077191
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я думаю что её так назвали потому что такой класс функций очень хорош, очень. Особенно на фоне O(n^2). А может быть тут super в смысле "выше", "больше", "сверх".
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39077224
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryБольше мы её(реализацию) никак не сократим/оптимизируем ?
В плане алгоритма нечего сокращать.

Можно return`ы поубирать, сделать вложенные if(), пару строк убавится, но имху предыдущий вариант читабельнее
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
// С сортировкой и массивом счетчиков
void with_counter(int* data, int* res)
{
	timer t;
	int** idx = (int**)malloc(TEST_SIZE * sizeof(int*)); // индексы сортировки
	if(!idx) {
		printf("No memory\n");
	} else {
		// Заполнение индексов и сортировка
		for(int i = 0; i < TEST_SIZE; i++) idx[i] = &data[i];
		qsort(idx, TEST_SIZE, sizeof(int*), compare_int);
		// Выбор шага счетчика
		int step = (int)sqrt((double)TEST_SIZE);
		// Массив счетчиков
		int cnt_size = TEST_SIZE / step + 1;
		int* cnt = (int*) malloc(cnt_size * sizeof(int)); // счетчики
		if(!cnt) {
			printf("No memory\n");
		} else {
			for(int i = 0; i < cnt_size; i++) cnt[i] = step;
			// Расчет
			int min = *idx[0];
			for(int i = 0; i < TEST_SIZE; i++) {
				int pos = idx[i] - data; // индекс минимального элемента в data
				 // обнуление посчитанного
				data[pos] = min;
				int cnt_pos = pos / step; // индекс счетчика
				cnt[cnt_pos]--;
				// Подсчет от начала группы
				for(int j = cnt_pos * step; j < pos; j++) {
					if(data[j] > data[pos]) res[pos]++;
				}
				// Добавление полных групп
				for(int j = 0; j < cnt_pos; j++) {
					res[pos] += cnt[j];
				}
			}
			free(cnt);
		}
		free(idx);
	}
	printf("Counters %s\n", t.value());
}


Можно комментарии и скобки {} поубирать кое-где. Типа
Код: plaintext
1.
for(int j = cnt_pos * step; j < pos; j++) if(data[j] > data[pos]) res[pos]++;


Но так вообще нечитаемый изврат получится.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39079303
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Версия 2.0 с деревом счетчиковИсходные данные не портит.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
#include <vector>
#include <algorithm>
bool compare_int_ptr(int* i,int* j) { return (*i < *j); }

// С сортировкой и деревом счетчиков
void with_counter_tree(int* data, int* res)
{
	timer t;
	// Подготовка индекса с порядком обхода
	std::vector<int*> idx(TEST_SIZE);
	for(int i = 0; i < TEST_SIZE; i++) idx[i] = &data[i];
	std::sort(idx.begin(), idx.end(), compare_int_ptr);
	// Инициализация дерева счетчиков
	std::vector<std::vector<int>> cnt_tree;
	for(int i = 2; i < TEST_SIZE; i <<= 1) {
		cnt_tree.resize(cnt_tree.size() + 1);
		cnt_tree.back().resize(TEST_SIZE / i + 1);
	}
	int cnt_levels = cnt_tree.size();
	// Расчет
	for(int i = 0; i < TEST_SIZE; i++) {
		int pos = idx[i] - data; // индекс минимального элемента в data
		res[pos] = pos;
		// подсчет кол-ва меньших data[pos]
		for(int l = 0, j = pos - 1; l < cnt_levels && j >= 0; l++) {
			j >>= 1;
			do {
				res[pos] -= cnt_tree[l][j];
				j--;
			} while((j & 1) == 0);
		}
		// увеличение счетчиков заполненных
		for(int l = 0, j = pos; l < cnt_levels; l++) {
			j >>= 1;
			cnt_tree[l][j]++;
		}
	}
	printf("Counters tree %s\n", t.value());
}


Ломал я голову как добавить дерево, пришел к мысли что массив счетчиков надо заменить на дерево счетчиков.
Смысл такой:
Строим дерево счетчиков: 0-й уровень один счетчик на 2 элемента, 1-й на 4, 2-й на 8 и т.д. пока на последнем не останется один счетчик. В итоге суммарно счетчиков столько же сколько элементов.
Вставляем на места по одному элементу исходных в порядке возрастания и по счетчикам определяем сколько уже вставлено. Затем Увеличиваем на 1 счетчики всех уровней соответствующие данному элементу.

Таким образом для подсчета с одного уровня читается не более двух счетчиков и переход на следующий уровень.
По сложности: в среднем на один элемент 1.5*log2(N/2) чтений счетчиков и log2(N/2) инкрементов.

Время расчета
ЭлементовВерсия 1.0Версия 2.01 млн.0.52 сек.0.21 сек.2 млн.1.4 сек.0.44 сек.3 млн.2.5 сек.0.66 сек.4 млн.3.78 сек.0.89 сек.8 млн.10.4 сек.1.84 сек.
Сложность улучшилась - скорость возросла.

PS Упрощать код дальше некуда. Взял <vector> чтобы читабельнее было. Если заменить на обычные массивы - будет быстрее работать в 1.5 раза, но кода для инициализации будет заметно больше.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39079338
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий, спасибо. А можно пожалуйста для особо одаренных описать алгоритм по пунктам. Я не очень хорошо его понял.
PS
попытался свой алгоритм замерить по скорости, на больших значениях не получается. Вероятно что-то не так с памятью
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39079357
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Там две части:
1. Элементы добавляются по возрастанию их значения. Т.е. сначала минимальный, затем следующий и т.д.
Для этого надо знать в каком порядке они идут. Для этого массив idx из указателей. Массив сортируется в порядке значений по указателям.
Код который касается порядка обхода
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
void with_counter_tree_1(int* data, int* res)
{
	// Подготовка индекса с порядком обхода
	std::vector<int*> idx(TEST_SIZE);
	for(int i = 0; i < TEST_SIZE; i++) idx[i] = &data[i];
	std::sort(idx.begin(), idx.end(), compare_int_ptr);
	// Расчет
	for(int i = 0; i < TEST_SIZE; i++) {
		int pos = idx[i] - data; // индекс минимального элемента в data
		printf("insert data[%d] = %d\n", pos, data[pos]);
	}
}


Результат на твоем набореinsert data[3] = 1
insert data[4] = 3
insert data[5] = 4
insert data[1] = 5
insert data[6] = 7
insert data[9] = 8
insert data[10] = 9
insert data[7] = 11
insert data[0] = 17
insert data[2] = 18
insert data[8] = 85

Дальше исходим из аксиомы: если мы вставили значение в ячейку X то ячеек от начала с большим значением всего X-1 - количество заполненных, т.к. заполнены только с меньшими значениями.
Т.к. нумерация с нуля, то res[pos] = pos; это и есть X-1 и остается посчитать сколько уже заполнено. Тут и нужны счетчики чтобы не делать перебора до начала.

До этого места понятно расписал?

Со счетчиками хз как объяснить попроще, сейчас соображу и распишу.
...
Рейтинг: 0 / 0
Мера неупорядоченности линейного массива и некоторые вопросы
    #39079370
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Про счетчики. Давай на твоем примере из 11 значений.
Получается три уровня счетчиков. 0й 6 счетчиков, 1й - 3, 2й - 2.
Таблица как счетчики соотносятся с конкретными элементами исходного массива.
Уровень012345678910000112233445100001111222200000000111
Колонки - номера исходных элементов, строки - уровни счетчиков, тело таблицы номер счетчика данного уровня.
Если приглядишься, то увидишь что каждый счетчик нижнего уровня включает в себя ровно два счетчика верхнего. Это и есть дерево.

Значения (переменная pos) обрабатываются по одному, поэтому сначала подсчет сколько до него, затем вставка его для последущей обработки. Наоборот нельзя (вставка потом подсчет), т.к. 0й уровень для двух значений. Так же будет неправильно работать если исходный набор будет содержать повторы. Это можно полечить сделав 0й уровень для одного элемента. В нашем случае это лишняя трата памяти. Дальше в обратном порядке чтобы понятнее было.

Вставка значения:
Код: plaintext
1.
2.
3.
4.
		for(int l = 0, j = pos; l < cnt_levels; l++) {
			j >>= 1;
			cnt_tree[l][j]++;
		}


cnt_levels количество уровней.
Перебираем все слои и в каждом увеличиваем счетчик на 1. Например для вставки data[3] будут увеличены счетчики cnt_tree[0][1], cnt_tree[1][0], cnt_tree[2][0] т.е. в общем виде номер счетчика на уровне определяется по формуле pos/2^(Уровень+1)

Получения количества вставленных с начала:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
		for(int l = 0, j = pos - 1; l < cnt_levels && j >= 0; l++) {
			j >>= 1;
			do {
				res[pos] -= cnt_tree[l][j];
				j--;
			} while((j & 1) == 0);
		}


берем pos-1 находим для него счетчик нулевого уровня, если начало счетчика не совпадает с началом счетчика следующего уровня, то берем предыдущий счетчик, начала уровней совпало - переходим на уровень выше и берем там предыдущий и т.д.
Например для подсчета сколько перед data[7] складываем cnt_tree[0][3]+cnt_tree[0][2]+cnt_tree[1][0]
для data[8] можно сразу взять cnt_tree[2][0], но у меня посчитается cnt_tree[0][3]+cnt_tree[0][2]+cnt_tree[1][0]
data[9] будет cnt_tree[0][4]+cnt_tree[1][1]+cnt_tree[1][0] хотя можно было cnt_tree[0][4]+cnt_tree[2][0]
Небольшой задел для оптимизации есть :)

Принцип понятен?
...
Рейтинг: 0 / 0
25 сообщений из 53, страница 1 из 3
Форумы / C++ [игнор отключен] [закрыт для гостей] / Мера неупорядоченности линейного массива и некоторые вопросы
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]