Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

Почему языки C-семейства заняли свое “особое” место среди прочих языков программирования [закрыт]

#c


А как вы считаете почему языки C-семейства заняли свое "особое" место среди прочих
языков программирования?
    


Ответы

Ответ 1



Думаю, они «взлетели» на феноменальном успехе самого языка C. Язык C был успешен потому, что во время его разработки была нужда в хорошем низкоуровневом системном языке. C оказался самым популярным среди них не в последнюю очередь потому, что семейство UNIX-подобных операционных систем выбрало C в качестве системного языка. (Что неудивительно, учитывая участие Денниса Ритчи в обоих проектах.) Высокоуровневых языков было достаточно в те далёкие времена (LISP, COBOL, Basic), но компиляторы того времени не были способны адекватно оптимизировать высокоуровневый код. C же снискал славу «кроссплатформенного ассемблера»: код на нём можно было легко оптимизировать вручную. Затем, C++ был пожалуй первым хорошим (для своего времени) языком программирования, ориентированным на практическое программирование. Совместимостью с C он обязан как своему взлёту и популярности, так и многим неустранимым своим недостаткам. Далее, вступает MS-DOS. Программисты Microsoft вдохновлялись UNIX-системами, и решили выбрать C как системный язык. Далее, долгое время de facto-стандартом для написания оконных систем под Windows стал, естественно, C++, как подходящий последователь C. Таким образом, две популярные платформы (UNIX и DOS/indows) приветствовали знание C и его потомков, что конечно же привело к их широкому распространению. Тем не менее, и у других языков был шанс. Например, более высокоуровневый Pascal был очень популярен в среде разработчиков под DOS, и планировался вначале как системный язык Windows. И он, кажется, был языком выбора под платформой Macintosh. (До тех пор, пока там предпочтительным языком не стал Objective C — снова язык из семейства C.) Pascal вплоне мог перехватить лидирующую роль в семействе языков практического программирования. Но, к сожалению, язык не развивался адекватно нуждам программистов, и был в основном вытеснен своими коллегами, основанными на синтаксисе C. Стоит заметить, однако, что последователи C, унаследовав синтаксис C, вовсе не унаследовали его стиль и философию.

Ответ 2



Вообще @Vlad довольно хорошо изложил с исторической точки зрения, но я зайду с другой стороны, грамматической/синтаксической. Любой не эзотерический язык программирования это так или иначе выбор между: Lisp C Forth. Функциональной, императивной, конкатенативной(стэковой) парадигмой: Префиксной + a b, инфиксной a + b, постфиксной a b + записью выражений. Так вот C(и остальные языки семейства, последовавшие за ним позднее) просто стал одним из первых общеупотребительных языков, который практически идеально и без лишнего синтаксического сахара вписывается в императивную парадигму. Он просто является её прямым и логичным выражением. Запись a + b выглядит понятнее для большинства людей, чем конкурирующие Lispовое + a b и Forthовое a b +. (Конечно все эти 3 нотации были известны еще задолго до изобретения Lisp C и Forth, но в общее употребление они вошли похоже именно в те времена) Кроме того он достаточно прост, в нем по мимо обычного потока управления есть всего 6 основных средств построения абстракций: указатели, функции, структуры, указатели на функции, препроцессор и typedef. Остальные элементы вроде фигурных скобок: {{}{}{}{}{}} Составных присваиваний: a+=a++ + --a; Указателей: int *(*f)(char *с) Смотрятся просто веселее и читаются легче чем Lisp с его унылыми эзотерическими пугающими людей круглыми скобками: (+ (* 3 (+ (* 2 4) (+ 3 5))) (+ (- 10 7) 6)) или Forth с Йоды магистра речью умной: : FLOOR5 ( n -- n' ) DUP 6 < IF DROP 5 ELSE 1 - THEN ;. Что касается других старых императивных языков вроде Pascal, BASIC, Fortran, то в них просто слишком много сахара и они все же не так изящны и выразительны как C. Можно например чисто визуально сравнить 4 Hello World для весьма похожих языков Pascal, Visual Basic, Fortran и С. Что вы выберете? Program HelloWord; begin writeLn ('Hello World!') end. Module Hello Sub Main() MsgBox("Hello, World!") End Sub End Module Program Hello Print *, "Hello World!" End Program Hello #include main() { printf("Hello World"); } P.S. Конечно в обычном BASIC можно сделать так ? "Hello, World!", но ничто ведь не мешает Вам: #define $ printf

Ответ 3



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

Ответ 4



Началось все, скорее всего, именно с синтаксиса. Из низкоуровневых языков, компилировавшихся в оптимальный код, C для своего времени был самым удобным. Замена всевозможных длиннющих BEGIN и END на { и } (и т.д.) сыграла немаловажную роль. Это сейчас у нас IDE с autocomplete и прочими ништяками, а тогда сокращение количества букаф в программах было для многих великой радостью. Этим и другими плюсами к себе завоевал расположение C.

Сравнение двух списков на нахождение элементов которые соответствуют правилам

#python #алгоритм #python_3.x #list


Вопрос заключается в том, что например у меня есть два списка:

lst1 = ['1', '2' , '3' , '4']
lst2 = ['123', '234' , '345' , '334']


Как мне найти такие элементы во втором списке, которые включают в себя только те
элементы, которые есть в первом, но, если в первом списке есть одна единица, то например
элемент "112" с второго списка не подходит.

Tо есть результатом программы должен быть 

ls3 = ['123', '234']


'345' - не подошло потому что там есть элемент "5" которого нет в первом списке

'334' - не подошло потому что там есть два элемента "3", а в первом списке элемент
"3" есть только один
    


Ответы

Ответ 1



Если известно как определить, можно ли составить строку word из заданных символов chars, используя каждый символ в chars не более его числа повторений (известен can_build(word, chars) предикат), то задача сводится к: result = list(filter(can_build, lst2)) или более читаемо: result = [word for word in lst2 if can_build(word)] где can_build использует chars = lst1 внутри. Если бы требовалось использовать все символы из chars, тогда это была бы проверка является ли word анаграммой chars, к примеру: "просветитель" можно получить перестановкой букв "терпеливость". Можно использовать похожие решения, когда точное равенство заменено на "не более". can_build() можно реализовать, найдя является ли word мультимножество подмножеством chars мультимножества. Если бы все символы в chars и word были уникальны, то can_build = set("1234").issuperset collections.Counter реализует идею множества, в котором элементы могут повторяться, то есть мультимножества. Как показано в элегантном решении в ответе @Timofey Bondarev, эту коллекцию можно использовать чтобы реализовать can_build: can_build = lambda word, chars=Counter(lst1): not (Counter(word) - chars) Можно реализовать тот же алгоритм вручную, не используя collections.Counter. from collections import defaultdict def Counter(letters): counts = defaultdict(int) for letter in letters: counts[letter] += 1 return counts chars_count = Counter(chars) def can_build(word): return all(chars_count[char] >= count for char, count in Counter(word).items()) Можно использовать простой список, если все символы принадлежат какому-либо алфавиту, тогда так как chars всё время один и тот же, то можно закэшировать chars.count значения. Например, если chars может содержать только цифры 0-9: from string import digits chars_count = [(digit, chars.count(digit)) for digit in digits] def can_build(word): return all(word.count(digit) <= count for digit, count in chars_count) это O(N * M) решение (M=len(digits)—размер алфавита), в отличии от O(N) решения, использующего Counter(). Если алфавит нефиксированный: alphabet = set(word), тогда это O(N**2) (квадратичный) алгоритм. Если alphabet фиксированный как в примере, то это O(N) (линейное) решение. Для небольшого алфавита, например, для boolean цифр (alphabet=(0,1)) или ДНК-строк (alphabet="GTAC"), это решение могло быть даже быстрее решения с Counter(). Ещё пример применения: если word, chars это числа, представленные их простыми множителями (например: (2,2,3) представляет 12, (5,7) представляет 35), тогда can_build() отвечает на вопрос является ли word делителем chars, то есть верно ли что: chars % word == 0. Q: как реализовать код, если избавиться от условия о том, что элемент должен повторяться столько раз сколько его есть в первом списке??? Уже ответ в этом предположении работает. Иначе это был бы случай с анаграммами: количество повторений совпадает. Решения с Counter() и с <=, >= НЕ требуют, чтобы элемент повторялся "столько раз сколько его есть в первом списке" (можно меньше). Если вы имеете ввиду, что количество повторений вообще не важно, а интересует только есть элемент или нет, то ситуация аналогична случаю когда все элементы уникальны, то есть: can_build = set(lst1).issuperset как уже упомянуто выше.

Ответ 2



Эту проблему можно решить, используя стандартный класс для мультимножества Counter: from collections import Counter lst1 = ['1', '2' , '3' , '4'] lst2 = ['123', '234' , '345' , '334'] base = Counter(lst1) result = [s for s in lst2 if not (Counter(s) - base)] Условие not (Counter(s) - base) проверяет то, что в мультимножестве s не больше элементов, чем в base

Ответ 3



[l2 for l2 in lst2 if all(l2.count(l) <= lst1.count(l) for l in set(l2))]

Ответ 4



#!/usr/bin/env python3.4 # -*- coding: utf-8 -*- lst1 = ['1', '2' , '3' , '4'] lst2 = ['123', '234' , '345' , '334'] S1 = set(lst1) S2 = set(lst2) S3 = set() for x in S2: if set(x) <= S1: S3.add(x) lst3 = list(S3) for x in lst3: for y in x: if x.count(y) > 1: count = lst3.index(x) continue lst3.pop(count) print(lst3) Если просто по логике вещей, без особых премудростей. Чуток знать о множествах (из школы), циклы и списки. Ответ ['123', '234']

Ответ 5



Решение выше, конечно более коротко, но по человечески можно сделать так: список = ['1', '2', '3', '4'] список2 = ['123', '234', '345', '34'] результат = [] for сочетание in список2: временный_список = список[:] # копия списка for цифра in сочетание: if цифра in временный_список: временный_список.remove(цифра) # чтобы не более одной проверки на число else: break else: результат.append(сочетание) print(результат) ===================== RESTART: C:\Python 3.5\задачка.py ===================== ['123', '234', '34']

Как работает оператор else if и в чем отличие от if?

#любой_язык #условия


Чем отличается оператор else if от обычного if ? 

Цепочка операторов из if-else if

if (condition)
    statement;
else if (condition)
    statement;
else if (condition)
    statement;


Цепочка операторов из if

if (condition)
    statement;
if (condition)
    statement;
if (condition)
    statement;


Есть ли между ними разница в работе?
    


Ответы

Ответ 1



Достаточно рассмотреть простой пример, чтобы понять, в чем заключается разница. int x = 0; if ( x == 0 ) { System.out.printline( "x = " + x ); ++x; } else if ( x == 1 ) { System.out.printline( "x = " + x ); ++x; } else if ( x == 2 ) { System.out.printline( "x = " + x ); ++x; } Вывод на консоль будет x = 0 А если этот код переписать в виде if ( x == 0 ) { System.out.printline( "x = " + x ); ++x; } if ( x == 1 ) { System.out.printline( "x = " + x ); ++x; } if ( x == 2 ) { System.out.printline( "x = " + x ); ++x; } то вывод на консоль будет x = 0 x = 1 x = 2 То есть в первом случае предложения if выполняются в зависимости от условий, а во втором случае они выполняются безусловно, то есть не зависит от выполнения предыдущих if предложений.

Ответ 2



Есть. Формат if/else if гарантирует, что при выполнении какого-либо из условий блоки с другими условиями не будут выполнены. При использовании цепочки if это не гарантируется. Например: int a = 10; if( a > 1 ) System.out.println( "Переменная 'a' больше 1" ); if( a > 5 ) System.out.println( "Переменная 'a' больше 5" ); Будут выполнены оба блока, и выведется: Переменная 'a' больше 1 Переменная 'a' больше 5

Ответ 3



Как такового оператора else if нет, это лишь использование ещё одного if в ветке else другого if. Но разница между ними есть. В первом случае второе условие отработает, если не отработает первое, а третье - если не отработает второе. Во втором случае отработают все условия (если где-то не возникнет, скажем, исключение). НО. Судя по вашему коду, условия у вас одинаковые. Поэтому в первом случае сработает тоьлко первый if или не сработает ничего. А во втором либо сработают все три if, либо не сработает ничего

Ответ 4



Во втором случае у вас проверяются всегда все условия. В первом случае если выполнится первое условие остальные уже не проверяются

Ответ 5



Вообще, конструкция if (condition) statement; else if (condition) statement; else if (condition) statement; чаще всего применяется, когда нужно в зависимости от того, чему равно проверяемое значение, выполнить несколько вариантов действий. И вообще по-идее в таких ситуациях нужно пользоваться оператором ветвления (switch\case\select и т.д). Но во многих языках он обладает неприятными ограничениями, которые заставляют пользоваться вышеупомянутой конструкцией. Зачем вставлять else, если логика последующих if такова, что проверку пройдёт только один? Чтобы избежать лишних проверок и явно указать читателю исходного кода, что здесь реализован по сути оператор ветвления. В некоторых языках реализован специальный оператор elseif\elsif\elif позволяющий избегать лишнего вложения блоков.

Ответ 6



В if вы даёте условие и если оно не выполняется можете вызвать else . Но в случаях когда нужно проверять условие одно за одним можно использовать конструкцию else if . И тогда в else if вы вписание ещё одно условие . int x = 1; if(x==3){ System.out.println("Это число 5");//к сожелению не выведет }else if(x==2){ System.out.println("Это число 2");//к сожелению не выведет }else if(x==1){ System.out.println("Это число 1");//выведет вот это }else { System.out.println("Число вообще не ходит"); }

Ответ 7



Так же можно воспользоватся оператором множественного выбора switch(). int value; cin >> value; switch(value){ case 1: return value+1; case 2: return value+2; default: return value; }

Ответ 8



Да, в else-if варианте переход к следующей ветке происходит только тогда, когда предыдущая дала результат false. int a = 1 int b = 2 int c = 3 if (a == 1) statement; else if (b == 2) statement; // эта ветка не будет выполнена else if (c == 3) statement; // эта ветка не будет выполнена Если Вы уберете все else-if, все три ветки будут выполнены. Поэтому else-if используется как последовательность, которая должны быть проверена, если предыдущая дала неверный результат.

Как вы делаете WinForms интерфейсы?

#c_sharp #winforms #.net #visual_studio


Привет, Хэшкод!
Хочу поинтересоваться у людей, делающих интерфейсы на WinForms, как обстоят их дела
и легко ли им живётся.
В чём собственно проблема. У разработчика интерфейса на WinForms есть три пути:

Довольствоваться стандартными контролами.
Писать свои.
Скачивать/покупать сторонние.

Первый варинт, отпадает сразу, как только начинаешь вглядываться в стандартные контролы,
предоставляемые visual studio. Нет, лично я ничего не имею против хорошо причёсанных
минималистичных приложений, использующих только лишь родные кнопочки, комбобоксы и
пр., но по личному опыту, с каждым годом после выхода 2007-го офиса, юзеры всё больше
и больше воротят нос от 'скучных' компонентов.
На написание собственных контролов я готов пойти в случае долговременного проекта,
когда дело действительно того стоит. В остальных же случаях хотелось бы готовых решений.
На сторонние компоненты возлагалось больше всего надежд, но как я понял они либо
платные либо слеплены на скорою руку в вырви-глаз стиле. Может я что-то пропустил,
и в свободном доступе есть масса хороших коллекций контролов? Ведь популярная технология,
должны быть решения.
В качестве ответа на вопрос меня вполне устроит либо ссылка на бесплатную коллекцию
контролов, либо фраза «WinForms метрв».    


Ответы

Ответ 1



Согласно моему ИМХО, я бы рекомендовал плавно переходить на WPF. Стандартные возможности по интеграции WPF и WinForms в одном приложении позволяют это делать буквально "плавно" и безболезненно. А в чем преимущество WPF? В контексте вашего вопроса, на нем довольно легко делать нескучные компоненты. Во-первых, и приятно, что самому делать проще, во-вторых, это же приводит к тому, что сообщество активно их создает под бесплатными лицензиями, и необходимости кому-то платить или самому мучиться нет.

Ответ 2



Как оказалось, на codeproject имеется весьма неплохая коллекция бесплатных контролов. Они, конечно, уступают платным решениям, но при усердном поиске и правильном использовании начинают выглядеть весьма прилично, и что самое главное сочетаться друг с другом. Собственно сама коллекция.

Ответ 3



Внешний вид и неприятные баги Visual Studio 2010 отбивают всякое желание переходить на WPF. Используем платные компоненты. MapXtreme для карт, DevExpress для всего остального.

Ответ 4



Стандартные очень даже ничего, для создания своих необходимы крепкие знания иерархии контролов .NET. Про другие - Сеть полна бесплатными и довольно качественными контролами, гугл да сурсфордж да кодпрожект в помощь...

Что нужно знать для практического использования С++? [закрыт]

#c++


Допустим, я изучил с++. Однако это всего лишь консольные приложения, классы, ссылки,
указатели, работа с файлами и т.д.

Как писать драйвера? (в какую сторону смотреть, есть ли книги или документация).
Что вообще следует писать на с++ и "как, чем"? Какой API лучше использовать для написания
оконных приложений и стоит ли это делать на с++?

Вот я и не могу понять, как это происходит, ведь в книгах с++ этого нет. Надеюсь
вы поняли ход моих мыслей и сможете прояснить. Спасибо.
    


Ответы

Ответ 1



На мой взгляд одна из самых актуальных проблем современной литературы - много фундаментального и мало более профессионального. Я постараюсь объяснить это так, как вижу сам. Хотя знаний у меня не так уж много, но по крайней мере я попытаюсь. Надеюсь знающие люди подправят, если ошибся. Итак, работа электроники происходит посредственном передачи данных по шинам от одного управляющего устройства другому(процессоры, микропроцессоры, микроконтроллеры и т.д.). В зависимости от того, какие данные переданы, электроника выполняет определенные задачи. Фактически это уровень архитектуры компьютера и ассемблера, которую очень хорошо бы знать на базовом уровне(Тоненбаум - "Архитектура компьютера"). Команды эти обычно объединяются в своего рода блоки, аналогично функциям, которые делают какую-то определенную цель. Иногда создаются целые кучи функций, которые объединяются в библиотеки функций(те самые math, iostream и т.д.). Низкий уровень, драйвера: Для написания драйвером надо посылать эти самые команды управляющим устройствам. Язык С++ не очень подходит под эти задачи, проще пользоваться чистым Си. В самом простом случае подключается какая-то библиотека, которая позволяет посылать значения устройствам и читать их. К примеру, простейший способ обращения к динамику ПК может выглядеть так(учтите, windows не разрешает напрямую обращаться к портам!): #include main( ) { int store; store = inp (97); /* запоминание начального значения с помощью порта 97 */ printf("пopт 97 = %d \n", store); /* проверка результатов*/ outp(97, 79); /* посылает 79 в порт 97; включение громкоговорителя */ outp(97, store); /* восстановление начального значения */ } Правда добрые люди обычно делают сторонние библиотеки более высокого уровня, которые уже содержат список готовых функций, классов, структур для обращения к устройствам. Например, код выше прекрасно работает из библиотеки функций windows.h, вызывая функцию beep(). Если вы хотите писать драйвера, то вам придется читать о том, как обращаться к устройству из документации разработчиков с оффициальных сайтов, какие команды устройство может выполнять, какие данные надо для этого посылать, есть ли для этого специальные библиотеки от разработчиков и многое другое. Высокий уровень. Люди, которые используют С++, работают обычно с готовыми наборами библиотек или создают свои под те или иные задачи. Например, есть такая вещь как WinAPI для создания графического интерфейса Windows окон. Это опять же готовые функции, вы подключаете библиотеку и работаете с ними. В ответ они делают какую-то работу и выдают результат. Если вы посмотрите на минимальное окно, то в коде вы увидите заполнение знакомых элементов: структуры, вызов функций, использование циклов и т.д. Другое дело, что эти функции и структуры сделали разработчики и они выполняют определенную работу, Вам остается только их вызывать. Для ознакомления с ними есть документация. Все выше и выше. Обычно библиотеки делаются универсальными и их можно использовать повторно. Например, на основе одной можно создать другую которая будет выполнять еще более специализированные функции. Например, можно создать библиотеку с функцией рисования линий. Из нее библиотеку с функцией рисования геометрических фигур. Из нее библиотеку с рисование деталей и т.д. так наращивается функциональность. Есть например программное рисование графики на мониторе, в windows его надстройка winAPI(линии, окошки, цвета уже готовы), но и у него есть более высокоуровневая оболочка MFC или Windows Forms, которые являются более усовершенствованными, но основаными на все том же WinAPI. Как итог: фактически работа программиста заключается в том, что бы выяснить какая библиотека может помочь справиться с нужными задачи и изучить ее. А изучив применить к своим нуждам. Хотите использовать Windows GUI приложения - есть WPF и Windows Form, или на более низком уровне взять WinApi. Хотите использовать видеокарту для 3D приложений - изучайте DirectX. Хотите работать с музыкой, опять же всякие библиотеки типа OpenAL есть в распоряжение. Охота писать драйвера, ищите информацию о том, как можно обращаться к устройствам и посредством какой библиотеки. Хотите узнать почему используются готовые библиотеки, изучите архитектуру компьютера и самые основы ассемблера. В любом случае вы будете пользоваться основными изученными элементами языка С++: структуры, функции, классы, указатели, простые типы данных и т.д. P.S. Пока писал, уже засомневался в правильности написанного)) Советую еще посмотреть вот этот сайт: http://www.firststeps.ru/

Ответ 2



как верно заметил Alexey123 С\С++ довольно много где применяется и объём знаний и практических навыков может кардинально отличаться... из вообще обязательного для любого программиста это: желательное для большинства программистов (хорошо знать основы) уметь читать С\С++ (простые несложные примеры) знать как собирается программа С\С++ понимать как работает процессор и память обязательное для всех программистов, лично я считаю что лучше это осваивать на примере С++, так как там можно сравнить функциональный и ООП стиль и заодно сравнить по скорости. знать и уметь вычислять Big O notation знать структуры данных: массив, вектор, список, очереди, деревья(бинарные, суффиксные, AVL, RB) знать что такое шаблоны знать контейнеры (реализации выше озвученных структур данных) более высокий уровень знать ООП и паттерны проектирования IDE + дебаггеры системы контроля версий синтаксические анализаторы профайлеры что касается GUI в связке с C++, то нужно держаться подальше от C++ Builder (крайне обидно когда вас громким матом выгоняют с собеседования, через пару секунд после показа проекта в "C++ Builder"). крайне полезно уметь писать на C++ для unix, если хотите писать только под винду то полезно покодить чутка в winapi но глубоко влезать нет смысла на начальном этапе. по GUI в С++ наилучший вариант это QT так как он win+unix при этом он открыт и довольно качественно сделан, за границей при разработке GUI в C++ он наиболее востребователен, однако его незаслуженно обходят стороной в наших учебных заведениях. лично мне видится (по вакансиям и по знакомству с языками программирования (ЯП)) что GUI постепенно исчезает в C++ (исключение QT) и переходит в более высокоуровневые языки С#, Java, так как там проще и дешевле создавать сложные проекты (по сравнению с C\C++), при этом C\C++ ни сколько не умирает, а просто переходит в другую плоскость работы в виде связки C\C++ + (Java,C#) например в виде "нативная часть" + "обёртка" (android) или в виде "низкоуровневый графический движок" + "высокоуровневая игра"

Ответ 3



Посмотрите например здесь: Написание драйверов под Windows / Программирование драйверов и здесь: Книги по C++ и Си

Ответ 4



Как писать драйвера? (в какую сторону смотреть, есть ли книги или документация). Читай в инете материал по DDK. Это по разработке драйверов. Что вообще следует писать на с++ и "как, чем"? Все, что пожелаешь. Ограничения ставит лишь фантазия. Какой API лучше использовать для написания оконных приложений и стоит ли это делать на с++? Взаимодействие между окнами в Windows идет через winApi. Тут придется разобраться с основами. Чтобы знать кухню изнутри. Все остальные либы взаимодействия это прослойка над winApi. Например, легко можно создать оконное приложение использовав Wtl или Mfc. PS А вообще самое лучшее найти друга или компанию и к ним затисаться. 2 месяца и ты начнешь плавать в материале. :-)

Разница между .h и .hpp

#c++


В чем разница между файлами с расширениями .h и .hpp в C++? Что лучше использовать? 
    


Ответы

Ответ 1



Строго говоря разницы между ними нет совсем. Разница может быть лишь в том, как IDE их интерпретирует. .h, если следовать некоторой логике, это заголовок для C(.c), тогда как .hpp это заголовок для C++(.cpp, .cxx etc.). Но всё это условности и зависят от предпочтений и используемых IDE. К примеру, в Visual Studio мы имеем .h и .cpp файлы, по умлочанию. Единственно, на мой взгляд, где различное именование может принести пользу, это в проекте, где сочетается C и C++ код. Тогда стоит иметь .h/.c для C-кода и .hpp/.cpp для C++ когда. Других применений, кроме персональных предочтений, я не вижу.

Ответ 2



Принципиальной разницы в поведении препроцессора в зависимости от расширения хедера нет. По большей части ему плевать на расширение, он включит файл с любым расширением или без него. Разные расширения для С-ных и С++-ных хедеров сделаны скорее для удобства программистов. Если видите хедер .hpp - даже не пытайтесь включать его в проект, написанный на чистом С, потому что, скорее всего, в нём будут описаны структуры, характерные только для C++ (классы, шаблоны и т. п.). Хедер .h можно (с оглядкой на то, что C не является подмножеством C++) включать в проекты, написанные на C++. Также IDE может применять различные правила форматирования и подсветки синтаксиса для хедеров .h и .hpp.

Простой способ предсказания следующего значения (марковский предиктор, “муравьиный” алгоритм, рекурсия Левинсона-Дарбина)

#алгоритм


Здравствуйте!
Имеется числовой ряд, программно некоторый массив длины N. Необходим самый простой
алгоритм предсказания следующего значения N+1, основанный на всех предыдущих значениях.
Подскажите пожалуйста такой.
Мой ряд представлен случайными положительными дробными числами. В общем любые несложные
идеи хотелось бы рассмотреть.
UPD::откуда берутся числа?
Есть 50 равноудаленных приемо-передатчиков (ПП), не важно как, но любой может связаться
с любым (полносвязная топология). Есть абстрактные источник и приемник (которые тоже
могут связаться с любым из ПП). Связь между источником и приемником всегда устанавливается
через 5 ПП (источник и приемник естественно не учитываются). В один момент времени,
например раз в сутки, связь (маршрут/путь) переформировывается по некоторым, для нас
неизвестным, случайным (именно случайным, так как, например, некоторые ПП в данный
момент времени недостижимы по какой-то причине) законам. Необходимо предсказать, какой
путь (или 6-10 других вариантов путей) будет выбран следующим основываясь лишь на знании
предыдущих выборов системы.
Много различных алгоритмов я уже перепробовал. Так как нету статистической зависимости
между двумя различными выборами путей (а если и есть, то ее сложно определить, я даже
боюсь утверждать, что смена пути происходит по, например, нормальному распределению
вероятности, но, думаю, что можно предположить это для данной задачи), вот я и пытаюсь
теперь аппроксимировать на основе "весов" маршрутов (произведения "весов" каждой пары
ПП, извлекаемых из матрицы переходов (веса определены на пересечении строки и столбца:
переход из i-го ПП в j-й), построенной по похожему принципу, описанному @northerner,
но более упрощенному).

Так как веса путей вида |1,2,3,4,5 | 1,3,2,4,5 | 5,4,3,2,1|, равны, нехитрые рассчеты
дают чуть больше 2млн. возможных маршрутов. Для каждого из них я вычисляю вес (по матрице).
Аналогично мы можем посчитать вес маршрута для каждого предыдущего уже выбранного системой
маршрута (промежуток - год, для каждого дня в году). Предсказывая поведение значения
веса, мы из двух миллионов выберем 6-10 вариантов наиболее схожих с нашим предсказанным
значением.
На данный момент я дошел лишь до того, что предположив нормальное распределение мы
можем выделить наиболее редко встречающиеся пары в матрице переходов. Например, есть
пары, которых за год еще ни разу не встречалось (у которых в матрице переходов на пересечении
- нуль их приходится заменять так как они портят картину, как я считаю). Тем не менее
эти пары не начинают пока появляться чаще остальных.

Если есть альтернативные идеи/методы предсказания маршрутов, буду рад. Если есть
идеи, как установить зависимость между сменами маршрутов, распределение и другие характеристики,
тоже буду рад.    


Ответы

Ответ 1



Можно попробовать построить примерный вариационный ряд и создать новое число как случайную величину, распределенную так же. То есть разбить множество возможных значений [0; 1] на n промежутков (скажем, на 10), подсчитать, сколько значений попадает в каждый и создать случайную величину, у которой такое же распределение вероятностей.

Ответ 2



Выбор предиктора очень сильно зависит от специфики последовательности. Если количество разных чисел, которые могут встретиться в последовательности невелико, предлагаю простой марковский предиктор. Будем считать, что элементы последовательности принимают значения от 1 до N (иначе их можно перенумеровать и будет так). Назовем возможные значения элемнтов состояниями. Пусть мощность множества состояний (количество разных значений) равна K. Определим матрицу P размером KxK и два одномерных массива R и C размером K. Получив очередной элемент последовательности X(N), увеличиваем на единицу значения P(X(N - 1), X(N)), R(X(N - 1)) и C(X(N)). Таким образом постепенно накапливаются значения: P(I, J) - количество переходов из I в J, R(I) - количество уходов из I, C(J) - количество приходов в J. P(I, J) / R(I) - оценка вероятности (частота), находясь в I перейти в J. Накопив достаточное количество переходов, можно начать "предсказывать": пусть мы находимся в состоянии Z. Разыгрываем случайную величину, равномерно распределенную в [0, 1]. Идем по строке Z слева направо, вычитая P(Z, J) / R(Z). Как только получаем нуль или меньше, стобец, в котором находимся и есть ожидаемый элемент. Метод хорошо работает при следующих ограничениях: число состояний действительно невелико; процесс стационарен (однороден по времени), то есть, если где-то в начале последовательности из 15 всегда переходили в 42, а в оставшейся части - ни разу, смысла использовать метод нет; следующий элемент существенно зависит от текущего (и гораздо менее - от совсем старых); вероятности перехода распределены достаточно неравномерно (в противном случае имеем белый шум и никакой предиктор ничего не даст). В описанном алгоритме C(J) оказались не нужны, но иногда требуются, поэтому оставлю и их.

Ответ 3



UPD Один из лучших прогностических методов - авторегрессионный. Во всяком случае, периодические всплески он выловит. Модель сигнала В основе метода - модель авторегрессии (АР) порядка k для выборки i = 0, 1, ..., n-1: xi+f0xi-1 + f1xi-2 + f2xi-3 + ... + fk-1xi-k = 0, где i=k, k+1,..., n-1. Порядок модели должен примерно соответствовать сложности сигнала. Тёплицева симметрия Согласно методу наименьших квадратов, следует минимизировать невязку: EPS = < (xi + f0xi-1 + f1xi-2 + f2xi-3 + ... + fkxi-k)2 > i = k..n-1, для чего следует приравнять к нулю частные производные от невязки по коэффициентам авторегрессии fs для s=0..k-1. Это приводит к следующей системе уравнений специального вида: a0f0 + a1f1 + a2f2 + ... + ak-2fk-2 + ak-1fk-1 = -a1, a1f0 + a0f1 + a1f2 + ... + ak-3fk-2 + ak-2fk-1 = -a2, a2f0 + a1f1 + a0f2 + ... + ak-4fk-2 + ak-3fk-1 = -a3, ... ak-2f0 + ak-3f1 + ak-4f2 + ... + a0fk-2 + a1fk-1 = -ak-1, ak-1f0 + ak-2f1 + ak-3f2 + ... + a1fk-2 + a0fk-1 = -ak, где a - автокорреляционная функция (АКФ). Первая особенность - вид матрицы в левой части, когда совпадают коэффициенты на главной диагонали и все коэффициенты на диагоналях, равноотстоящих от главной. Матрицы с симметрией такого вида называются тёплицевыми, и к ним применим алгоритм Левинсона. Вторая особенность - в том, что столбец свободных членов сформирован из элементов той же матрицы a, и к такой системе применим алгоритм Левинсона-Дарбина. Третья особенность - что на самом деле тёплицевой данная матрица является лишь приблизительно. Например, для модели второго порядка точный вид уравнений такой: < xi-12 > f0 + < xi-1xi-2 > f1 = < xixi-1 >, < xi-1xi-2 > f0 + < xi-22 > f1 = < xixi-2 >, где индекс i в каждой сумме вида < ui > пробегает значения от 2 до максимума. Нетрудно заметить, что: Элементы на главной диагонали соответствуют значениям нулевого элемента автокорреляционной функции, выборки для вычисления которых сдвинуты на один элемент. Элементы на побочной диагонали и первый элемент в столбце свободных членов соответствуют значениям первого элемента автокорреляционной функции, причём элементы на диагонали равны, а выборка для свободного члена сдвинута по отношению к ним. Это может привести к неожиданным эффектам для последовательностей с сильным возрастающим (убывающим) трендом. Так, для выборки из первых 10 членов последовательности Фибоначчи xi = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55} получается система уравнений вида: 1869 f0 + 1155 f1 = - 3024, 1155 f0 + 714 f1 = - 1869 с правильным решением f0 = -1, f1 = -1, но ожидаемой тёплицевой симметрии задачи нет. При этом использование АКФ даёт систему 4893 f0 + 3024 f1 = - 3024, 3024 f0 + 4893 f1 = - 1869, использование которой в данном случае неправомерно. Основной метод борьбы с этими эффектами - центрирование выборки. Но главное - это проверка возможности серьёзного упрощения модели и применения алгоритма Дарбина. Алгоритм Дарбина Суть идеи Дарбина - поиск решения для матрицы размерности n+1 в виде: (f'0, f'1, ... , f'n-1, f'n) = (f0, f1, ... , fn-1, 0) + beta (fn-1, fn-2, ... , f0, 1). Действительно, подстановка этого решения в матрицу размерности n+1 с учётом системы размерности n даёт: -a1 - beta an + anbeta = -a1, -a2 - beta an-1 + an-1beta = -a2, ... -an-1 - beta a1 + a1beta = -an, anf0 + an-1f1 + an-2f2 + ... + a2fn-2 + a1fn-1 + beta(anfn-1 + an-1fn-2 + an-2fn-3 + ... + a2f1 + a1f0 + a0) = -an+1. Первые n уравнений системы удовлетворяются при любом значении beta, последнее - при beta = - (an+1 + anf0 + an-1f1 + an-2f2 + ... + a2fn-2 + a1fn-1) / (anfn-1 + an-1fn-2 + an-2fn-3 + ... + a2f1 + a1f0 + a0). Функция durbin() возвращает массив векторов авторегрессии для всего диапазона n. При n=1 f = (-a1/a0), последующие векторы коэффициентов вычисляются рекуррентно (сначала beta, затем f). Программная реализация В программе на языке PHP реализованы следующие функции: Центрирование массива center(). Скалярное произведение векторов со сдвигом второго вектора scalar_prod(). Вывод массива с текстовым комментарием print_array(). Вывод системы линейных уравнений второго порядка и её решения print_s(). Вычисление автокорреляционной функции (АКФ) acf(). Сравнение точной и тёплицевой систем второго порядка и их решений для заданной выборки compare_s(). Алгоритм Дарбина для решения системы уравнений специального вида durbin(). Тестирование алгоритма Дарбина test_durbin(). Текст программы function center(&$arr){ $len = count($arr); $aver = array_sum($arr) / $len; foreach($arr as &$item){ $item -= $aver; } } function scalar_prod($a, $b, $shift = 0, &$c = null){ $scal = 0; if(is_null($c)) $cc = []; else $cc = &$c; foreach($a as $key => $item){ $cc[] = $item * $b[$key+$shift]; $scal += end($cc); } return $scal; } function print_array($arr, $str, $n = 11){ print $str."["; foreach($arr as $key => $item){ if(!(($key+1) % $n)) print "
"; printf ("\"%03d\" => %.3f, ", $key, $item); } print "]"; } function print_s($a, $b, $str){ print("

$str"); printf("
%.3f f0 + %.3f f1 = %.3f", $a[0][0], $a[0][1], $b[0]); printf("
%.3f f0 + %.3f f1 = %.3f", $a[1][0], $a[1][1], $b[1]); $det = $a[0][0]*$a[1][1] - $a[1][0]*$a[0][1]; $det0 = $b[0]*$a[1][1] - $b[1]*$a[0][1]; $det1 = $a[0][0]*$b[1] - $a[1][0]*$b[0]; printf("
Решение: f = [%f, %f]", (float)$det0 / $det, (float)$det1/$det); } function acf($flow, $k, $center = -1, $len = null){ if(is_null($len)){ $len = count($flow); } $slice = array_slice($flow, $k, $len-$k); for($lag = 0; $lag <= $k; $lag++){ $result[$lag] = scalar_prod($slice, $flow, $k-$lag); } if($center != -1){ $denom = 1.0/$result[0]; foreach($result as &$res){ $res *= $denom; } } return $result; } function compare_s($test){ $m = count($test); $acf2 = acf($test, 0, -1, $m-2); $acf1 = acf($test, 1, -1, $m-1); $acf = acf($test, 2); $a_exact = [ [$acf1[0],$acf1[1]], [$acf1[1],$acf2[0]] ]; $a = [ [$acf[0],$acf[1]], [$acf[1],$acf[0]] ]; $b = [-$acf[1], -$acf[2]]; print_s($a_exact, $b, "Контроль симметрии матрицы

Точная система (порядок 2):"); print_s($a, $b, "Тёплицева система (порядок 2):"); } function durbin($acf, $n){ $ff = []; $f = [-$acf[1]/$acf[0]]; print_array($f, "
f = "); $ff[] = $f; for($r = 1; $r < $n; $r++){ $acr = array_reverse(array_slice($acf, 0, $r+1)); $fr = array_reverse($f); $fr[] = 1; $f[] = 0; $beta = - ($acf[$r+1] + scalar_prod($f, $acr))/scalar_prod($fr, $acr); print("
beta[$r] = $beta "); $f = array_map(function($a,$b) use($beta){ return $a+$beta*$b; },$f,$fr); $ff[] = $f; print_array($f, "  f = "); } return $ff; } function test_durbin($arr, $n, $center=0){ $len = count($arr); if($center){ center($arr); } $eps_arr = 0; foreach($arr as $item){ $eps_arr += $item*$item; } printf("

Решение по Дарбину (порядок АР = %d, длина выборки = $len, СКО выборки = %f):", $n, sqrt($eps_arr/($len-1))); $a = acf($arr, $n); print_array($a, "

АКФ: "); $ff = durbin($a,$n); $c = array_reverse(end($ff)); $eps = 0; $brr = []; for($j=$n; $j<$len; $j++){ $brr[$j] = $arr[$j]+scalar_prod($c, $arr, $j-$n); $eps += pow($brr[$j],2); } $k = count($f)-1; printf("
СКО остатка = %f", sqrt($eps/($len-$n))); } $m = 10; $test = [1.0, 1,0]; for ($i = 2; $i < $m; $i++){ $test[$i] = $test[$i-1] + $test[$i-2]; } print("*** Фибоначчи - 10: ***"); compare_s($test); test_durbin($test, 2); print("

*** Фибоначчи-10 с центрированием: ***"); test_durbin($test, 2, 1); $m=2000; $dpi = 2*M_PI; for($j=0; $j<$m; $j++) $arr[$j] = sin($j);// - $dpi*floor($j/$dpi)); print("

*** Авторегрессия по Дарбину (синусная выборка): ***"); compare_s($arr); test_durbin($arr, 1); test_durbin($arr, 2); test_durbin($arr, 3); test_durbin($arr, 4); test_durbin($arr, 5); test_durbin($arr, 6); Результаты: *** Фибоначчи - 10: *** Контроль симметрии матрицы Точная система (порядок 2): 1869.000 f0 + 1155.000 f1 = -3024.000 1155.000 f0 + 714.000 f1 = -1869.000 Решение: f = [-1.000000, -1.000000] Тёплицева система (порядок 2): 4893.000 f0 + 3024.000 f1 = -3024.000 3024.000 f0 + 4893.000 f1 = -1869.000 Решение: f = [-0.618007, -0.000030] Решение по Дарбину (порядок АР = 2, длина выборки = 10, СКО выборки = 23.321426): АКФ: ["000" => 4893.000, "001" => 3024.000, "002" => 1869.000, ] f = ["000" => -0.618, ] beta[1] = -2.98035943134E-5   f = ["000" => -0.618, "001" => -0.000, ] СКО остатка = 15.285024 *** Фибоначчи-10 с центрированием: *** Решение по Дарбину (порядок АР = 2, длина выборки = 10, СКО выборки = 17.795443): АКФ: ["000" => 2496.320, "001" => 1399.520, "002" => 716.420, ] f = ["000" => -0.561, ] beta[1] = 0.0398418808262   f = ["000" => -0.583, "001" => 0.040, ] СКО остатка = 12.412102 *** Авторегрессия по Дарбину (синусная выборка): *** Контроль симметрии матрицы Точная система (порядок 2): 999.018 f0 + 539.793 f1 = -539.750 539.793 f0 + 999.016 f1 = 415.712 Решение: f = [-1.080605, 1.000000] Тёплицева система (порядок 2): 998.969 f0 + 539.750 f1 = -539.750 539.750 f0 + 998.969 f1 = 415.712 Решение: f = [-1.080619, 1.000008] Решение по Дарбину (порядок АР = 1, длина выборки = 2000, СКО выборки = 0.707169): АКФ: ["000" => 999.677, "001" => 539.750, ] f = ["000" => -0.540, ] СКО остатка = 0.595153 Решение по Дарбину (порядок АР = 2, длина выборки = 2000, СКО выборки = 0.707169): АКФ: ["000" => 998.969, "001" => 539.750, "002" => -415.712, ] f = ["000" => -0.540, ] beta[1] = 1.00000781118   f = ["000" => -1.081, "001" => 1.000, ] СКО остатка = 0.000009 Решение по Дарбину (порядок АР = 3, длина выборки = 2000, СКО выборки = 0.707169): АКФ: ["000" => 998.142, "001" => 538.985, "002" => -415.712, "003" => -988.206, ] f = ["000" => -0.540, ] beta[1] = 0.999521483275   f = ["000" => -1.080, "001" => 1.000, ] beta[2] = 0.925724185475   f = ["000" => -0.154, "001" => 0.000, "002" => 0.926, ] СКО остатка = 0.000488 Решение по Дарбину (порядок АР = 4, длина выборки = 2000, СКО выборки = 0.707169): АКФ: ["000" => 998.122, "001" => 538.857, "002" => -415.831, "003" => -988.206, "004" => -652.029, ] f = ["000" => -0.540, ] beta[1] = 0.999342177677   f = ["000" => -1.079, "001" => 0.999, ] beta[2] = 0.925843078378   f = ["000" => -0.154, "001" => 0.000, "002" => 0.926, ] beta[3] = 6.00603640925   f = ["000" => 5.406, "001" => 0.000, "002" => 0.000, "003" => 6.006, ] СКО остатка = 0.004358 Решение по Дарбину (порядок АР = 5, длина выборки = 2000, СКО выборки = 0.707169): АКФ: ["000" => 997.550, "001" => 538.964, "002" => -415.143, "003" => -987.569, "004" => -652.029, "005" => 282.984, ] f = ["000" => -0.540, ] beta[1] = 0.999977661062   f = ["000" => -1.081, "001" => 1.000, ] beta[2] = 0.925422594935   f = ["000" => -0.155, "001" => 0.000, "002" => 0.925, ] beta[3] = 5.96426000454   f = ["000" => 5.364, "001" => 0.000, "002" => 0.000, "003" => 5.964, ] beta[4] = -1.11184318911   f = ["000" => -1.267, "001" => -0.000, "002" => 0.000, "003" => 0.000, "004" => -1.112, ] СКО остатка = 0.000027 Решение по Дарбину (порядок АР = 6, длина выборки = 2000, СКО выборки = 0.707169): АКФ: ["000" => 996.630, "001" => 538.238, "002" => -415.008, "003" => -986.697, "004" => -651.222, "005" => 282.984, "006" => 957.015, ] f = ["000" => -0.540, ] beta[1] = 0.999627453765   f = ["000" => -1.080, "001" => 1.000, ] beta[2] = 0.925654012051   f = ["000" => -0.155, "001" => 0.000, "002" => 0.926, ] beta[3] = 5.98719502289   f = ["000" => 5.387, "001" => 0.000, "002" => 0.000, "003" => 5.987, ] beta[4] = -1.11131942365   f = ["000" => -1.266, "001" => -0.000, "002" => -0.000, "003" => 0.000, "004" => -1.111, ] beta[5] = -0.877666481891   f = ["000" => -0.291, "001" => -0.000, "002" => -0.000, "003" => 0.000, "004" => -0.000, "005" => -0.878, ] СКО остатка = 0.000360 Программа показала высокую эффективность на ограниченной (синусной) выборке при большом объёме исходных данных.

Ответ 4



Ваш массив элементов можно представить как значение некоторой функции в точках 1, 2, 3... N Тогда, для поиска нового значения (с аргументом функции N+1), можно использовать интерполирование по Ньютону. Программирует это довольно просто. Так мы найдём многочлен P(x), который принимает значение a[i] из массива в ячейке с индексом i (т.е. P(i) = a[i]), тогда, чтобы найти a[N+1] значение, просто подставим в этот многочлен вместо x, значение N+1. Т.е. ответ = P(N+1).

Ответ 5



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

Ответ 6



Кроме нейронной сети есть еще такой интересный алгоритм: муравьиный :) Применив к вашей системе, он будет работать как-то так: предположим, что источнику сигнала при попытке соединения могут дать следующие ответы: подтвеждение готовности передачи или отказ. При передаче - увеличиваем "привлекательность" ПП на какую-то величину "А", при отказе - уменьшаем на "Б". "А", "Б"- надо найти экспериментальным путем для оптимальной работы системы. Кроме того надо будет настроить максимумы для счетчиков "привлекательности" и уменьшения счетчика со временем.