Python 糟糕的数学或糟糕的编程,也许两者都有?

Python 糟糕的数学或糟糕的编程,也许两者都有?,python,geometry,python-imaging-library,Python,Geometry,Python Imaging Library,我正在编写一个Python程序,作为一个个人项目,从著名的海因莱因小说中生成“月球自由州”标志。我一直在网上抄袭纹章规则和匹配数学公式,但在我的bendsinister例程中显然出了问题,因为未注释时断言失败。左弯的面积应该是旗帜总面积的1/3,而不是。我所做的唯一真正狡猾的事情就是猜测梯形高度的公式,但我猜误差可能在任何地方。我删掉了大部分代码,只留下了显示问题所需的内容。希望数学上没有那么困难的人能发现错误 #!/usr/bin/python 'generate bend sinister

我正在编写一个Python程序,作为一个个人项目,从著名的海因莱因小说中生成“月球自由州”标志。我一直在网上抄袭纹章规则和匹配数学公式,但在我的
bendsinister
例程中显然出了问题,因为未注释时断言失败。左弯的面积应该是旗帜总面积的1/3,而不是。我所做的唯一真正狡猾的事情就是猜测梯形高度的公式,但我猜误差可能在任何地方。我删掉了大部分代码,只留下了显示问题所需的内容。希望数学上没有那么困难的人能发现错误

#!/usr/bin/python
'generate bend sinister according to rules of heraldry'
import sys, os, random, math, Image, ImageDraw
FLAG = Image.new('RGB', (900, 600), 'black')
CANVAS = ImageDraw.Draw(FLAG)
DEBUGGING = True

def bendsinister(image = FLAG, draw = CANVAS):
 '''a bend sinister covers 1/3 of the field, sinister chief to dexter base

    (some sources on the web say 1/5 of the field, but we'll use 1/3)
    the "field" in this case being the area of the flag, so we need to
    find a trapezoid which is 1/6 the total area (width * height).

    we need to return only the width of the diagonal, which is double
    the height of the calculated trapezoid
 '''
 x, y = image.size
 b = math.sqrt((x ** 2) + (y ** 2))
 A = float(x * y)
 debug('%d * %d = %d' % (x, y, A))
 H = triangle_height(A / 2, b)  # height of triangular half of flag
 width = trapezoid_height(b, H, A / 6) * 2
 if command == 'bendsinister':
  show_bendsinister(x, y, width, image, draw)
 return width

def show_bendsinister(x, y, width, image = FLAG, draw = CANVAS):
 'for debugging formula'
 dexter_base, sinister_chief = (0, y), (x, 0)
 draw.line((dexter_base, sinister_chief), 'blue', int(width))
 image.show()
 debug(image.getcolors(2))  # should be twice as many black pixels as blue

def triangle_height(a, b):
 'a=bh/2'
 h = float(a) / (float(b) / 2)
 debug('triangle height: %.2f' % h)
 return h

def trapezoid_height(b, H, a):
 '''calculate trapezoid height (h) given the area (a) of the trapezoid and
    base b, the longer base, when it is known that the trapezoid is a section
    of a triangle of height H, such that the top, t, equals b when h=0 and
    t=0 when h=H. h is therefore inversely proportional to t with the formula
    t=(1-(h/H))*b, found simply by looking for what fit the two extremes.
    the area of a trapezoid is simply the height times the average length of
    the two bases, b and t, i.e.: a=h*((b+t)/2). the formula reduces
    then to (2*a)/b=(2*h)+(h**2)/H, which is the quadratic equation
    (1/H)*(h**2)+(2*h)-((2*a)/b)=0; solve for h using the quadratic formula
 '''
 try:
  h = (-2 + math.sqrt(4 - 4 * (1.0 / H) * -((2 * a) / b))) / (2 * (1.0 / H))
  debug('trapezoid height with plus: %.2f' % h)
 except:  # must be imaginary, so try minus instead
  h = (-2 - math.sqrt(4 - 4 * (1.0 / H) * -((2 * a) / b))) / (2 * (1.0 / H))
  debug('trapezoid height with minus: %.2f' % h)
 t = (1 - (float(h) / H)) * b
 debug('t=%d, a=%d, check=%d' % (t, round(a), round(h * ((b + t) / 2))))
 #assert round(a) == round(h * ((b + t) / 2))
 return h

def debug(message):
 if DEBUGGING:
  print >>sys.stderr, message

if __name__ == '__main__':
 command = os.path.splitext(os.path.basename(sys.argv[0]))[0]
 print eval(command)(*sys.argv[1:]) or ''
以下是调试输出,显示我远离1/3区域:

jcomeau@intrepid:~/rentacoder/jcomeau/tanstaafl$ ./bendsinister.py 900 * 600 = 540000 triangle height: 499.23 trapezoid height with plus: 77.23 t=914, a=90000, check=77077 [(154427, (0, 0, 255)), (385573, (0, 0, 0))] 154.462354191 jcomeau@intrepid:~/rentacoder/jcomeau/tanstaafl$/bendsinister.py 900 * 600 = 540000 三角形高度:499.23 梯形高度加上:77.23 t=914,a=90000,检查=77077 [(154427, (0, 0, 255)), (385573, (0, 0, 0))] 154.462354191 以下是输出的图像,添加了一些行: 红线将两个三角形分开,其中一个可用于计算梯形。我用的是从左上角开始的那个。绿线是三角形的高度,即程序中的变量H



有关完成的脚本和标志(使用Michael Anderson提供的更正),请参阅。谢谢大家的帮助

将矩形分成两个三角形。它们将是相同的

黑色三角形+蓝色梯形是三角形A。 黑色三角形本身就是三角形B

三角形A和三角形B是相似的三角形,因此它们的面积由与其相关的比例因子的平方来关联

我们希望蓝色梯形是三角形A面积的三分之一(这样弯曲将占整个矩形的三分之一)。这意味着三角形B必须是三角形A面积的2/3。因此scalefactor必须是sqrt(2/3)


然后,您应该能够很容易地将其转换为折弯几何体的坐标。

我在空闲会话中执行了以下代码

from PIL import Image, ImageDraw
from math import sqrt

'generate bend sinister according to rules of heraldry'
import sys, os, random, math
FLAG = Image.new('RGB', (900, 600), 'black')
CANVAS = ImageDraw.Draw(FLAG)
DEBUGGING = True

def debug(message):
    if DEBUGGING:
        print >>sys.stderr, message


def show_bendsinister(x, y, width, image = FLAG, draw = CANVAS):
 'for debugging formula'
 dexter_base, sinister_chief = (0, y), (x, 0)
 print 'dexter_base==',dexter_base,'sinister_chief==',sinister_chief
 draw.line((dexter_base, sinister_chief), 'blue', int(width))
 image.show()
 debug(image.getcolors(2))  # should be twice as many black pixels as blue

def trapezoid_height(x, y, P):
 '''Given a rectangle whose width and length are (x) and (y)

The half of this rectangle is a large triangle A
whose base (b) is the diagonal of the rectangle
and its height (H) goes from its base (b) to
the right angle of the large triangle.
(x) and (y) are the side-lengths of the triangle.
The area of this large triangle is (x*y)/2 = (H*b)/2

Given a trapezoid whose base is the diagonal (b) of the rectangle
and base (b) of the large triangle, its height is (h)
and its top is (t).
Given (S) as the area of the trapezoid.
In general, the trapezoid is disymtric because the triangle have x != y.
So the area is S = h*(b + t)/2

This function trapezoid_height() calculates the height (h) of the trapezoid
in order that the trapezoid have an area (S) which must be
the percentage (P) of the area of the large triangle A. So:
h*(b + t)/2 = S = P*[H*b /2]  ==> h*(b + t) = P*H*b
==> h*t = P*H*b - h*b ==> h*t*(H-h) = [P*H - h]*b*(H-h)

The large triangle is the sum of the trapezoid and of a little triangle B
having an height equal to (H-h) and a base which is the top (t)
of the trapezoid.
The area of this little triangle B is t*(H-h)/2 and must be equal to (1-P)*[H*b / 2]
==> t*(H-h) = (1-P)*H*b ==> h*t*(H-h) = h*(1-P)*H*b

From h*t*(H-h) = [P*H - h]*b*(H-h)  and  h*t*(H-h) = h*(1-P)*H*b
we obtain [P*H - h]*b*(H-h) = h*(1-P)*H*b
==> b*h**2 - (b*H + xy)*h + P*x*y*H = 0
==> h**2 - 2*H*h + P*(H**2) = 0
That leads to the solution H*(1 - sqrt(1-P)), the other H*(1 + sqrt(1-P))
being bigger than H
'''

 H = math.sqrt( (x*x*y*y) / (x*x + y*y) )
 return H*(1 - sqrt(1-P))



def bendsinister(image = FLAG, draw = CANVAS):
 '''a bend sinister covers 1/3 of the field, sinister chief to dexter base

    (some sources on the web say 1/5 of the field, but we'll use 1/3)
    the "field" in this case being the area of the flag, so we need to
    find a trapezoid which is 1/6 the total area (width * height).

    we need to return only the width of the diagonal, which is double
    the height of the calculated trapezoid
 '''
 x, y = image.size
 print 'x ==',x,'y ==',y
 percentage = float(1)/3
 width = 2 * trapezoid_height(x, y , percentage)
 print 'height ==',width/2
 print 'width==',width


 if command == 'bendsinister':
  show_bendsinister(x, y, width, image, draw)
 return width

command = 'bendsinister'
print bendsinister()
结果

x == 900 y == 600
height == 91.6103029364
width== 183.220605873
dexter_base== (0, 600) sinister_chief== (900, 0)
[(180340, (0, 0, 255)), (359660, (0, 0, 0))]
183.220605873
显示的蓝色条纹不会给人留下字段面积的1/3的印象,但数字表明:

359660 / 180340 = 1.994344

也许这更适合我?蒂姆,你为什么这么认为?这是一个我试图解决的编程问题,而不是有效的东西,我正在努力改进。只是一种预感,基于目前为止没有答案和投票结果(不是我)。我看了FAQ,它说“不是关于:故障排除、调试或理解代码片段”。所以它不属于那里。缺乏答案很可能是因为这是一个困难的问题。至于结束投票:有些人喜欢出于任何原因结束其他人的问题。不同的堆栈交换站点之间有很多重叠。但我想这条评论应该属于meta.:)+1,这是关键点:为了按
2/3
缩放面积,必须按
sqrt(2/3)
缩放长度。这是你唯一需要的“二次”运算。谢谢,在我睡一会儿之后,这可能会更有意义+1现在无论如何都睡不着,必须试试。非常感谢,这就解决了问题。我仍然想知道我的方法哪里出了问题,但我希望在我死后能够解决这个问题:^@jcomeau_ictx sqrt(2/3)*H是三角形B的高度,所以梯形的高度是H-sqrt(2/3)*H=H*(1-sqrt(2/3))。这就是我通过解析代数公式
h**2-2*h*h+(2/3)*(h**2)=0得到的结果,在没有看到你优雅的推理的情况下,Michael+1保持了代数世界中的几何推理方式和分析所有问题的计算方式。我们对三角形面积和梯形面积都使用了相同的公式,但你的推导结果是正确的,而我的却没有。给你+1,尽管我昨晚从迈克尔·安德森那里得到了我需要的答案。谢谢@你完全正确:你使用了好的公式。在我开始对你的问题感兴趣的时候,我对这些问题的研究太肤浅了,当时我还没来得及想一想,我一定是搞糊涂了。我不知道我是否应该让我的大脑休息一下。