Что такое инвариант?


130

Это слово, кажется, используется во многих контекстах. Лучшее, что я могу понять, это то, что они означают переменную, которая не может измениться. Разве не для этого нужны константы / финалы (черт возьми, Java!)?


Может, надо было назвать это безвариантным?
джонни

Ответы:


189

Инвариант более «концептуален», чем переменная. В общем, это свойство состояния программы, которое всегда истинно. Говорят, что функция или метод, обеспечивающий выполнение инварианта, поддерживает его.

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

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


1
Этот отличный пример был бы лучше, если бы он включал ответ cero со ссылкой на википедию.
ablarg

2
Возраст родителей больше возраста их биологических детей. Окружающий контекст изменится, но инвариант никогда не изменится. Например, это может быть семья Смитов, у которой двое детей. Или это могло быть у других видов любого млекопитающего. Контекст меняется, но инвариант не меняется.
truthadjustr

1
"... свойство состояния программы, которое всегда истинно ..." - @jacob baskin - хорошо написано, и спасибо за это.
twknab

«... свойство состояния программы, которое всегда истинно ...» - я согласен, что это хорошо написано, но это может вводить в заблуждение. Сначала я понял это как логическое значение true, но я почти уверен, что это означает «... всегда константа» (но, возможно, это просто потому, что английский не мой родной язык)
dev-null

29

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


17

Я обычно рассматриваю их больше с точки зрения алгоритмов или структур.

Например, у вас может быть инвариант цикла, который можно утверждать - всегда истинно в начале или в конце каждой итерации. То есть, если ваш цикл должен был обрабатывать коллекцию объектов из одного стека в другой, вы могли бы сказать, что | stack1 | + | stack2 | = c, вверху или внизу цикла.

Если проверка инварианта не удалась, это будет означать, что что-то пошло не так. В этом примере это может означать, что вы забыли поместить обработанный элемент в последний стек и т. Д.


17

Магия википедии: инвариант (информатика)

В информатике предикат, который, если он истинен, будет оставаться истинным на протяжении определенной последовательности операций, называется () инвариантным по отношению к этой последовательности.


2
Ссылки? Технический жаргон? ЧТЕНИЕ? Какого черта ? ;) Если серьезно, хорошая ссылка, но было бы неплохо сделать небольшое резюме.
Dustman

10

Как говорится в этой строке:

В информатике предикат, который, если он истинен, будет оставаться истинным на протяжении определенной последовательности операций, называется () инвариантным по отношению к этой последовательности.

Чтобы лучше понять эту надежду, этот пример на C ++ помогает.

Рассмотрим сценарий, в котором вам нужно получить некоторые значения и получить их общее количество в переменной с именем as countи добавить их в переменную с именемsum

Инвариант (опять же это больше похоже на концепцию):

// invariant:
// we have read count grades so far, and
// sum is the sum of the first count grades

Код для приведенного выше будет примерно таким:

int count=0;
double sum=0,x=0;
while (cin >> x) {
++count;
sum+=x;
}

Что делает приведенный выше код?

1) Читает ввод из cinи помещает их вx

2) После одного успешного чтения увеличьте countиsum = sum + x

3) Повторяйте 1-2, пока чтение не остановится (например, ctrl + D)

Инвариант цикла:

Инвариант должен ВСЕГДА быть истинным . Итак, сначала вы начинаете свой код с этого

while(cin>>x){
  }

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

// we have read count grades so far, and

Как сохранить инвариант в силе?

Просто! счетчик приращения.

Так ++count;было бы хорошо !. Теперь наш код выглядит примерно так:

while(cin>>x){
 ++count; 
 }

Но

Даже сейчас наш инвариант (понятие, которое должно быть ИСТИННО) является ложным, потому что теперь мы не удовлетворили вторую часть нашего инварианта.

// sum is the sum of the first count grades

Так что же теперь делать?

Добавить xв sumи хранить его в sum( sum+=x) и в следующий раз cin>>xбудет читать новое значение в х.

Теперь наш код выглядит примерно так:

while(cin>>x){
 ++count; 
 sum+=x;
 }

Давай проверим

Соответствует ли код нашему инварианту

// invariant:
// we have read count grades so far, and
// sum is the sum of the first count grades

код:

while(cin>>x){
 ++count; 
 sum+=x;
 }

Ах !. Теперь инвариант цикла всегда равен True, и код работает нормально.

Приведенный выше пример был взят и изменен из книги Эндрю-Кёнинга и Барбары-Э. Ускоренный C ++.



2

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


2

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


2

Этот ответ предназначен для моего 5-летнего ребенка. Не думайте об инварианте как о постоянном или фиксированном числовом значении. Но может быть. Однако это еще не все.

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

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


1

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


0

В книге « Java Concurrency in Practice» есть отличный пример инварианта и его значение .

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

введите описание изображения здесь


0

Все ответы здесь отличные, но я чувствовал, что могу пролить больше света на этот вопрос:

Инвариант с языковой точки зрения означает то, что никогда не меняется. Хотя эта концепция на самом деле пришла из математики, это один из популярных методов доказательства в сочетании с индукцией.

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

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

Когда инвариант используется для предиката логики цикла, он называется инвариантом цикла . Его можно использовать вне циклов, но для циклов это действительно важно, потому что у вас часто есть много возможностей или бесконечное количество возможностей.

Заметьте, что я использую слово «предикат» логики компьютерной программы, а не доказываю. И это потому, что, хотя в математике инвариант может использоваться в качестве доказательства, он никогда не может доказать, что компьютерное программное обеспечение при выполнении даст то, что ожидается, из-за того, что программное обеспечение выполняется поверх многих абстракций, что никогда не может быть доказано. что они дадут то, что ожидается (подумайте, например, об аппаратной абстракции).

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.