2015-04-30 3 views
-5

Ниже два алгебраически эквивалентной формулы для параллельных резисторов:вычисление соединено параллельно сопротивления

par1(r1, r2) = (r1 * r2)/(r1 + r2), or 

par2(r1, r2) = 1/(1/r1 + 1/r2) 

После двух питона функций, каждые из которых вычисляет формулу parallel_resistors:

def par1(r1, r2): 
    return div_interval(mul_interval(r1, r2), add_interval(r1, r2)) 

def par2(r1, r2): 
    one = interval(1, 1) 
    rep_r1 = div_interval(one, r1) 
    rep_r2 = div_interval(one, r2) 
    return div_interval(one, add_interval(rep_r1, rep_r2)) 

Ниже приведен интервальная абстракция, которая используется вышеупомянутыми функциями par1 и par2.

def interval(a, b): 
    """Construct an interval from a to b. """ 
    return (a, b) 

def lower_bound(x): 
    """Return the lower bound of interval x. """ 
    return x[0] 

def upper_bound(x): 
    """Return the upper bound of interval x. """ 
    return x[1] 

def div_interval(x, y): 
    """Return the interval that contains the quotient of any value in x divided 
    by any value in y. 

    Division is implemented as the multiplication of x by the reciprocal of y. 
    >>> str_interval(div_interval(interval(-1, 2), interval(4, 8))) 
    '-0.25 to 0.5' 
    """ 
    assert (lower_bound(y) > 0 or upper_bound(y) < 0), "what it means to divide by an interval that spans zero" 
    reciprocal_y = interval(1/upper_bound(y), 1/lower_bound(y)) 
    return mul_interval(x, reciprocal_y) 


def str_interval(x): 
    """Return a string representation of interval x. 
    >>> str_interval(interval(-1, 2)) 
    '-1 to 2' 
    """ 
    return '{0} to {1}'.format(lower_bound(x), upper_bound(x)) 

def add_interval(x, y): 
    """Return an interval that contains the sum of any value in interval x and 
    any value in interval y. 
    >>> str_interval(add_interval(interval(-1, 2), interval(4, 8))) 
    '3 to 10' 
    """ 
    lower = lower_bound(x) + lower_bound(y) 
    upper = upper_bound(x) + upper_bound(y) 
    return interval(lower, upper) 

def mul_interval(x, y): 
    """Return the interval that contains the product of any value in x and any 
    value in y. 

    >>> str_interval(mul_interval(interval(-1, 2), interval(4, 8))) 
    '-8 to 16' 
    """ 
    p1 = lower_bound(x) * lower_bound(y) 
    p2 = lower_bound(x) * upper_bound(y) 
    p3 = upper_bound(x) * lower_bound(y) 
    p4 = upper_bound(x) * upper_bound(y) 
    return interval(min(p1, p2, p3, p4), max(p1, p2, p3, p4)) 

Тест результат:

>>> r1 = interval(1, 2) 
>>> r2 = interval(3, 4) 
>>> par1(r1, r2) 
(0.5, 2.0) 
>>> par2(r1, r2) 
(0.75, 1.3333333333333333) 

Мы заметили другой результат от par1 и par2, которые вычисляют различными, но алгебраически эквивалентных выражений. Для приведенных выше данных r1 и r2 ниже приведено вычисление.

par1 --> return mul_interval((3, 8), (1/6, 1/4)) = (1/2, 2) 
======= 
rep_r1 = div_interval((1, 1), (1, 2)) = (1/2, 1) 
rep_r2 = div_interval((1, 1), (3, 4)) = (1/4, 1/3) 
par2 --> return div_interval((1, 1), (3/4, 4/3)) = (3/4, 4/3) 

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

Правильно ли я понимаю?

+0

UpperBound должен быть 'х [-1]' Я думаю? – ZdaR

+0

@anmol_uppal Пожалуйста, проверьте и найдите ответ на свой вопрос. – overexchange

+0

Просьба дать основание для downvote, чтобы уточнить этот вопрос – overexchange

ответ

4

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

Настоящая проблема заключается в том, что в par1, ваши r1 и r2 интервалы отображаются с обеих сторон операции деления. Когда ваша функция деления вычисляет взаимность второго аргумента, она меняет порядок границ интервалов, поэтому верхняя граница входных данных влияет на нижнюю границу вывода.

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

Рассмотрите расчёт r/r, для интервала r = (a, b). Ваша функция div_interval будет вычислять результат как (a/b, b/a), когда базовой алгеброй мы знаем, что результат должен быть (1, 1) (так как любое число, деленное на себя, равно 1). Однако код деления должен предполагать, что каждый его аргумент не зависит от другого. Если дивизор оказывается на максимальной границе своего интервала (b), он может делить дивиденд на его минимальную границу (a). Он не может знать, что эти две вещи не могут произойти одновременно.

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

+0

Если я делаю это как 'reciprocal_y = interval (1/lower_bound (y), 1/upper_bound (y))' Это не имеет никакого значения. потому что мы выбираем 'min' и' max' в 'mul_interval' – overexchange

+0

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

+0

Как изменить 'par1' и' par2', чтобы получить аналогичные результаты? – overexchange

-1

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

def par1(r1,r2): 
    r1lower = float(r1[0]) 
    r1upper = float(r1[1]) 
    r2lower = float(r2[0]) 
    r2upper = float(r2[1]) 
    low = 1/((1/r1lower)+(1/r2lower)) 
    high = 1/((1/r1upper)+(1/r2upper)) 
    return(low, high) 

def par2(r1,r2): 
    r1lower = float(r1[0]) 
    r1upper = float(r1[1]) 
    r2lower = float(r2[0]) 
    r2upper = float(r2[1]) 
    low = (r1lower * r2lower)/(r1lower + r2lower) 
    high = (r1upper * r2upper)/(r1upper + r2upper) 
    return(low, high) 

if __name__ == '__main__': 
    print par1((1,2),(3,4)) 
    print par2((1,2),(3,4)) 

Выход:

(0.75, 1.3333333333333333) 
(0.75, 1.3333333333333333) 
[Finished in 0.1s] 

Где-то в вашем коде у вас есть ошибки ...

+0

par1/par2 исходит из [здесь] (http://www-inst.eecs.berkeley.edu/~cs61a/fa12/hw/hw3.html) – overexchange

+0

Ваш код получает правильный результат для 'par2' (используя формулу' par1' из вопроса), поскольку он предполагает (правильно, как выясняется), что нижняя граница для выхода получается из объединения нижних границ двух входных интервалов (и то же для верхних границ). Однако это не всегда верно для интервальных операций.Верхняя граница 'a/b' выполняется, например, нижней границей' b'. Это источник логической ошибки в коде. Никакой код интервала общего назначения не получит 'a/a' правильно. – Blckknght

+0

@Blckknght: Я понимаю сейчас. Полагаю, должно быть очевидно, что никто не напишет такой неясный код, кроме как академическое упражнение. – SiHa

Смежные вопросы