Ответ 1
(a + p <= b) && (a != 1) && (b - a - p != 1);
Один для математиков. Это обошло офис, и мы хотим узнать, кто может предложить лучшую оптимизированную версию.
(((a+p) <= b) && (a == 0 || a > 1) && (b >= p)) &&
((b - (a + p) == 0) || (b - (a + p) > 1))
Изменить: все данные являются положительными int
Изменить: лучше == реорганизован для простоты
(a + p <= b) && (a != 1) && (b - a - p != 1);
Если формула работает и исходит из ваших бизнес-правил, нет реальной необходимости ее упростить. Возможно, компилятор лучше знает, как оптимизировать формулу.
Единственное, что вам нужно сделать, это использовать имена переменных переменных, которые отражают бизнес-логику.
Остерегайтесь применения какого-либо предлагаемого решения до его тестирования.
Рефактор для простоты, введя более локальные переменные, которые указывают значение каждого выражения. Это трудно для нас сделать, не имея представления о том, что означает a, b и p.
b >= p && b != p+1
EDIT: Хорошо, это не сработало, но это делает:
a != 1 && b >= a+p && b-a-p != 1
(a!=1) && ((b==a+p) || (b>1+a+p))
Он может быть не самым простым, но должен быть одним из самых читаемых.
Я бы не сделал всю математику в этом выражении. Например, b - (a + p) оценивается дважды. Если возможно, разделите их на переменные.
Кроме того, при написании дерева изящного извещения можно оптимизировать его, все, что вы видите дважды, можно повторно использовать.
Поскольку все они являются положительными, много повторений можно удалить:
Итак, как первый шаг,
(((a+p) <= b) && (a == 0 || a > 1) && (b >= p)) && ((b - (a + p) == 0) || (b - (a + p) > 1))
становится
((a+p) <= b) && (a != 1) && (b >= p)) && ((b - (a + p) != 1)
Для ясности это просто заменяет шаблон (foo == 0 || foo > 1)
на foo != 1
Этот шаблон появляется дважды выше, один раз с foo = a и один раз с foo = (b - (a+p))
Так как ints без знака, (a == 0 || a > 1) можно заменить (a!= 1).
С первого прохода вы можете уменьшить его до этого:
uint sum = a + p;
return ((sum <= b) && (a != 1) && (b >= p)) && (b - sum != 1);
Кроме того, было бы гораздо более читаемым, если бы вы смогли дать более значимые имена переменным. Например, если a и p были давлениями, то a + p может быть заменено как PressureSum.
bap = b - (a + p)
bap >= 0 && bap != 1 && a != 1
EDIT: теперь у меня есть -2 для честной попытки помочь, а также для того, что мне кажется правильным. Для вас, кто может использовать Python, вот две функции: одна с вопросом и одна с моим ответом:
def question(a, b, p):
return (((a+p) <= b) and (a == 0 or a > 1) and (b >= p)) or ((b - (a + p) == 0) or (b - (a + p) > 1))
def answer(a, b, p):
bap = b - (a + p)
return bap >= 0 and bap != 1 and a != 1
s = a + p
b >= s && a != 1 && b - s - 1 > 0
Проверено, возвращает то же логическое значение, что и вопрос.
Программа, которую я использовал для проверки: (получал удовольствие писать ее)
#include <iostream>
using namespace std;
typedef unsigned int uint;
bool condition(uint a, uint b, uint p)
{
uint s = a + p;
return uint( b >= s && a != 1 && b - s - 1 > 0 )
== uint( (((a+p) <= b) && (a == 0 || a > 1) && (b >= p))
&& ((b - (a + p) == 0) || (b - (a + p) > 1)) );
}
void main()
{
uint i = 0;
uint j = 0;
uint k = 0;
const uint max = 50;
for (uint i = 0; i <= max; ++i)
for (uint j = 0; j <= max; ++j)
for (uint k = 0; k <= max; ++k)
if (condition(i, j, k) == false)
{
cout << "Fails on a = " << i << ", b = " << j;
cout << ", p = " << k << endl;
int wait = 0;
cin >> wait;
}
}
Это так просто, как я мог его получить.
def calc(a, b, p):
if (a != 1):
temp = a - b + p
if temp == 0 or temp < -1:
return True
return False
Он также может быть записан как:
def calc(a, b, p):
temp = a - b + p
return a != 1 and (temp == 0 or temp < -1)
Или как:
def calc(a, b, p):
temp = a - b + p
return a != 1 and temp <= 0 and temp != -1
Протестировано с помощью a, b, p от 0 до 10000:
a != 1 && a != (b-p-1) && a <= (b-p);
Я думаю, что его можно упростить еще больше.
мои извинения за ошибку в исходном деривации. Это происходит, когда вы не беспокоитесь о unit test после рефакторинга!
исправленный вывод следует в виде тестовой программы.
Короткий ответ:
((a > 1) && (skeet == 0)) || ((a > 1) && (jon > 0) && (skeet < -1));
где
jon = (b - p)
skeet = (a - jon);
class Program
{
static void Main(string[] args)
{
bool ok = true;
for (int a = 1; a < 100; a++)
{
Console.Write(a.ToString());
Console.Write("...");
for (int b = 1; b < 100; b++)
{
for (int p = 1; p < 100; p++)
{
bool[] results = testFunctions(a, b, p);
if (!allSame(results))
{
Console.WriteLine(string.Format(
"Fails for {0},{1},{2}", a, b, p));
for (int i = 1; i <= results.Length; i++)
{
Console.WriteLine(i.ToString() + ": " +
results[i-1].ToString());
}
ok = false;
break;
}
}
if (!ok) { break; }
}
if (!ok) { break; }
}
if (ok) { Console.WriteLine("Success"); }
else { Console.WriteLine("Failed!"); }
Console.ReadKey();
}
public static bool allSame(bool[] vals)
{
bool firstValue = vals[0];
for (int i = 1; i < vals.Length; i++)
{
if (vals[i] != firstValue)
{
return false;
}
}
return true;
}
public static bool[] testFunctions(int a, int b, int p)
{
bool [] results = new bool[16];
//given: all values are positive integers
if (a<=0 || b<=0 || p<=0)
{
throw new Exception("All inputs must be positive integers!");
}
//[1] original expression
results[0] = (((a+p) <= b) && (a == 0 || a > 1) && (b >= p)) &&
((b - (a + p) == 0) || (b - (a + p) > 1));
//[2] a==0 cannot be true since a is a positive integer
results[1] = (((a+p) <= b) && (a > 1) && (b >= p)) &&
((b - (a + p) == 0) || (b - (a + p) > 1));
//[3] rewrite (b >= p) && ((a+p) <= b)
results[2] = (b >= p) && (b >= (a+p)) && (a > 1) &&
((b - (a + p) == 0) || (b - (a + p) > 1));
//[4] since a is positive, (b>=p) guarantees (b>=(p+a)) so we
//can drop the latter term
results[3] = (b >= p) && (a > 1) &&
((b - (a + p) == 0) || (b - (a + p) > 1));
//[5] separate the two cases b>=p and b=p
results[4] = ((b==p) && (a > 1) && ((b - (a + p) == 0) ||
(b - (a + p) > 1))) || ((b > p) && (a > 1) &&
((b - (a + p) == 0) || (b - (a + p) > 1)));
//[6] rewrite the first case to eliminate p (since b=p
//in that case)
results[5] = ((b==p) && (a > 1) && ((-a == 0) ||
(-a > 1))) || ((b > p) && (a > 1) &&
(((b - a - p) == 0) || ((b - a - p) > 1)));
//[7] since a>0, neither (-a=0) nor (-a>1) can be true,
//so the case when b=p is always false
results[6] = (b > p) && (a > 1) && (((b - a - p) == 0) ||
((b - a - p) > 1));
//[8] rewrite (b>p) as ((b-p)>0) and reorder the subtractions
results[7] = ((b - p) > 0) && (a > 1) && (((b - p - a) == 0) ||
((b - p - a) > 1));
//[9] define (b - p) as N temporarily
int N = (b - p);
results[8] = (N > 0) && (a > 1) && (((N - a) == 0) || ((N - a) > 1));
//[10] rewrite the disjunction to isolate a
results[9] = (N > 0) && (a > 1) && ((a == N) || (a < (N - 1)));
//[11] expand the disjunction
results[10] = ((N > 0) && (a > 1) && (a == N)) ||
((N > 0) && (a > 1) && (a < (N - 1)));
//[12] since (a = N) in the first subexpression we can simplify to
results[11] = ((a == N) && (a > 1)) ||
((N > 0) && (a > 1) && (a < (N - 1)));
//[13] extract common term (a > 1) and replace N with (b - p)
results[12] = (a > 1) && ((a == (b - p)) ||
(((b - p) > 0) && (a < (b - p - 1))));
//[14] extract common term (a > 1) and replace N with (b - p)
results[13] = (a > 1) && (((a - b + p) == 0) ||
(((b - p) > 0) && ((a - b + p) < -1)));
//[15] replace redundant subterms with intermediate
//variables (to make Jon Skeet happy)
int jon = (b - p);
int skeet = (a - jon); //(a - b + p) = (a - (b - p))
results[14] = (a > 1) && ((skeet == 0) ||
((jon > 0) && (skeet < -1)));
//[16] rewrite in disjunctive normal form
results[15] = ((a > 1) && (skeet == 0)) ||
((a > 1) && (jon > 0) && (skeet < -1));
return results;
}
}
// In one line:
return (a != 1) && ((b-a-p == 0) || (b-a-p > 1))
// Expanded for the compiler:
if(a == 1)
return false;
int bap = b - a - p;
return (bap == 0) || (bap > 1);
Если вы публикуете процессор, который используете, я могу оптимизировать его для сборки. =]
jjngy здесь имеет это право. Здесь доказательство того, что его упрощенная формула эквивалентна оригиналу с помощью Coq Proof Assistant.
Require Import Arith.
Require Import Omega.
Lemma eq : forall (a b p:nat),
(((a+p) <= b) /\ ((a = 0) \/ (a > 1)) /\ (b >= p)) /\
((b - (a + p) = 0) \/ (b - (a + p) > 1)) <->
((a + p <= b) /\ ~ (a= 1) /\ ~ (b - a - p = 1)).
Proof. intros; omega. Qed.
Хорошо
((b - (a + p) == 0) || (b - (a + p) > 1))
Would be better writen as:
(b - (a + p) >= 0)
Applying this to the whole string you get:
((a+p) <= b) && (a > 1) && (b >= p)) && (b - (a + p) >= 0)
(a + p) <= b is the same thing as b - (a + p) >= 0
Итак, вы можете избавиться от этого:
((a+p) <= b) && (a > 1) && (b >= p))
Первая итерация:
bool bool1 = ((a+p) <= b) && (a == 0 || a > 1) && (b >= p);
bool bool2 = (b - (a + p) == 0) || (b - (a + p) > 1);
return bool1 && bool2;
Вторая итерация:
int value1 = b - (a + p);
bool bool1 = (value1 >= 0) && (a == 0 || a > 1) && (b >= p);
bool bool2 = (value1 == 0) || (value1 > 1);
return bool1 && bool2;
Третья итерация (все позитивы)
int value1 = b - (a + p);
bool bool1 = (value1 >= 0) && (a != 1) && (b >= p);
bool bool2 = (value1 == 0) || (value1 > 1);
return bool1 && bool2;
4-я итерация (все позитивы)
int value2 = b - p;
int value1 = value2 - a;
bool bool1 = (value1 >= 0) && (a != 1) && (b - p >= 0);
bool bool2 = (value1 == 0) || (value1 > 1);
return bool1 && bool2;
5-я итерация:
int value2 = b - p;
int value1 = value2 - a;
bool bool1 = (value1 >= 0) && (a != 1) && (value2 >= 0);
bool bool2 = (value1 == 0) || (value1 > 1);
return bool1 && bool2;
Хорошо, я надеюсь, что я сделал свою математику прямо здесь, но если я прав, то это упростит совсем немного. В конце концов, это не выглядит одинаково, но основная логика должна быть одинаковой.
// Initial equation
(((a + p) <= b) && (a == 0 || a > 1) && (b >= p)) && ((b - (a + p) == 0) || (b - (a + p) > 1))
// ((a + p) <= b) iif a = 0 && p = b; therefore, b = p and a = 0 for this to work
(b == p) && ((b - (a + p) == 0) || (b - (a + p) > 1))
// Simplification, assuming that b = p and a = 0
(b == p) && (a == 0)
Однако, если мы работаем в предположении, что ноль не является положительным или отрицательным, то это означает, что любое значение a, предоставленное уравнение будет больше или равно единице. Это, в свою очередь, означает, что уравнение всегда будет оцениваться как ложное из-за того, что следующее:
(a == 0 || a > 1)
Оценил бы только true, когда a >= 2; однако, если верно и следующее:
(b >= p)
Тогда это означает, что p не меньше, чем b, таким образом:
((a + p) <= b)
Подстановкой становится:
((2 + b) <= b)
Который явно не может оценить true.
Я добавил это как комментарий к ответу на nickf, но подумал, что предлагаю его в качестве ответа на него. Хорошие ответы кажутся вариацией его, в том числе и моей. Но поскольку мы не зависим от компилятора для оптимизации (если бы OP был, мы бы даже не делали этого), то кипячение этого вниз от 3 AND до следующего означает, что будут значения, где только 2 из 3 порций необходимо будет оценить. И если это выполняется в script, это будет иметь значение, в отличие от скомпилированного кода.
(a != 1) && ((b > (a + p + 1)) || (b == (a + p))))
Основываясь на комментарии, я добавлю, что это лучше, чем версия AND:
Я думаю, это зависит от того, превышает ли ваш набор данных истинных результатов более 50% наборов входных данных. Чем чаще ввод верен, тем лучше будет моя вариация. Итак, с этим уравнением, похоже, что стиль AND будет лучше (по крайней мере, для моего набора входных данных 0-500).
Если a, b и p - целые положительные числа (при условии, что положительный диапазон включает значение 0), тогда выражение (((a + p) <= b) && (a == 0 || a > 1) && (b >= p)) && & ((b - (a + p) == 0) || (b - (a + p) > 1)) может быть сведена до ((a + p) <= b) && (a!= 1) && ((b- (а + р))!= 1)
Позвольте мне продемонстрировать это: В первой части выражения есть условие ((a + p) <= b), что если оцениваемая истина возвращает true, вторая часть: ((b - (a + p) == 0) || (b - (a + p) > 1)). Если верно, что (b >= (a + p)), то (b - (a + p)) должно быть больше или равно 0, нам нужно чтобы убедиться, что (b- (a + p))!= 1. Отложите этот термин на некоторое время и продолжайте.
Теперь мы можем сосредоточить наши усилия на первой части (((a + p) <= b) && (a == 0 || a > 1) && (b >= p)) && & ((B- (а + р))!= 1)
Если a положительно, то оно всегдa >= 0, поэтому мы можем отказаться от теста (a == 0 || a > 1), если пользу ( a!= 1) и уменьшите первую часть выражения до (((a + p) <= b) && (b >= p) && (a!= 1)).
Для следующего шага сокращения вы можете считать, что если b >= (a + p), то, очевидно, b >= p ( a положительна), и выражение можно свести к
((a + p) <= b) && (a!= 1) && ((b- (а + р))!= 1)
b >= (a+p) && a>=1
even b >= p
является избыточным, так как это всегда будет иметь место для a >= 1
как насчет следующей логики, прокомментируйте это:
((a == 0 || a > 1) && ((b-p) > 1) )
(((a + p) <= b) && (a == 0 || a > 1) && & (b >= p)) && ((b - (a + p) == 0) || (b - (a + p) > 1))
1) (a == 0 || a > 1) является (a!= 1)
2) (b >= p) есть (b - p >= 0)
(a + p <= b) есть (b - p >= a), что сильнее, чем (b - p >= 0).
Первое условие сводится к (a!= 1) && (b - p >= a).
3) (b - (a + p) == 0) - (b - a - p == 0) - (b - p == a).
(b - (a + p) > 1) есть (b - a - p > 1) - (b - p > 1 + a).
Так как мы имели (b - p >= a), и мы используем && мы можем сказать, что (b - p >= a) охватывает (b - p == a & b - p > 1 + a).
Следовательно, все условие будет сведено к
(a!= 1 && (b - p >= a))
Там есть темп, чтобы уменьшить его до (b >= p), но это сокращение не будет охватывать запрет b = p + 1, поэтому (a!= 1 && (b - p >= a )) является условием.
Этот вопрос был довольно удовлетворительно дан уже на практике, но есть один момент, о котором я упоминаю ниже, который я еще не видел, когда кто-либо еще поднимает.
Так как нам сказали, что a >= 0, и первое условие гарантирует, что b - (a + p) >= 0, заключенный в квадрат || тесты могут быть превращены в тесты против неравенства с 1:
(a + p <= b) && (a!= 1) && (b >= p) && (b - a - p!= 1)
Заманчиво удалить проверку (b >= p), которая даст выражение nickf. И это почти наверняка правильное практическое решение. К сожалению, нам нужно больше узнать о проблемной области, прежде чем говорить, безопасно ли это сделать.
Например, если использовать C и 32-битные беззнаковые int для типов a, b и p, рассмотрим случай, когда a = 2 ^ 31 + 7, p = 2 ^ 31 + 5, b = 13. Мы имеем a > 0, (a + p) = 12 < b, но b < п. (Я использую '^' для указания экспоненциальности, а не побитового xor.)
Вероятно, ваши значения не будут приближаться к тем диапазонам, где этот тип переполнения является проблемой, но вы должны проверить это предположение. И если это окажется возможным, добавьте комментарий с этим выражением, объясняющим это, чтобы какой-то усердный оптимизатор будущего не беззаботно удалял тест (b >= p).
Я чувствую (a!= 1) && & (a + p <= b) && (a + p!= b - 1) несколько яснее. Другой вариант:
int t = b-p; (a!= 1 & a <= t & a!= t-1)
В основном a является либо 0, t, либо находится между 2 и t-2 включительно.
a!= 1 && ((b == a + p) || (b - p > a + 1))
(((a+p) <= b) && (a == 0 || a > 1) && (b >= p)) && ((b - (a + p) == 0) || (b - (a + p) > 1))
так как a >= 0 (положительные целые числа), член (a == 0 || a > 1) всегда истинен
if ((a + p) <= b) тогда (b >= p) истинно, когда a, b, p являются >= 0
поэтому ((a + p) <= b) && (a == 0 || a > 1) && (b >= p)) && ((b - (a + p) == 0) сводится к
b>=(a+p)
(b - (a + p) == 0) || (b - (a + p) > 1) эквивалентно b >= (a + p)
поэтому все уравнение сводится к
**b>= (a+p)**