Tính diện tích đa giác khi biết tọa độ của các đỉnh

Liên hệ QC

selves037

Thành viên thường trực
Tham gia
22/9/17
Bài viết
228
Được thích
66
Giới tính
Nam
Trong mặt phẳng tọa độ Oxy cho n điểm (n của em đang dùng từ 3,4,5,6 điểm) có tọa độ (x1,y1, x2, y2, x3, y3, xy, y4...). Em chờ các anh em trong diễn đàn viết một function tính diện tích theo tọa độ hình được tạo bởi n điểm trên. biết rằng các điểm trên luôn tạo thành 1 đa giác lồi:
JavaScript:
function  TinhDienTich(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5...){
    TinhDienTich=...
}
 
Trong mặt phẳng tọa độ Oxy cho n điểm (n của em đang dùng từ 3,4,5,6 điểm) có tọa độ (x1,y1, x2, y2, x3, y3, xy, y4...). Em chờ các anh em trong diễn đàn viết một function tính diện tích theo tọa độ hình được tạo bởi n điểm trên. biết rằng các điểm trên luôn tạo thành 1 đa giác lồi
Theo bạn thì bài này bạn tính tay ra sao?
 
Upvote 0
bạn dùng autocad thì nó dễ hơn nhiều đó bạn
 
Upvote 0
Bạn tham khảo mã dưới đây là mã tính diện tích đa giác các đường nối không cắt nhau, kể cả diện tích đa giác lõm

JavaScript:
Private Type pointXY
  X  As Single
  Y  As Single
End Type


Private Sub calcPolygonArea_test()
  Debug.Print calcPolygonArea(100, 100, 100, 300, 300, 400, 400, 250, 300, 0)
  Debug.Print calcPolygonArea(300, 300, 300, 100, 0, 0, -100, 400)
  Debug.Print calcPolygonArea(50, 150, 200, 50, 350, 150, 350, 250, 250, 320, 200, 250, 150, 350, 100, 250)
  Debug.Print calcPolygonArea(100, 100, 300, 100, 400, 300, 100, 300)
End Sub

Function calcPolygonArea(ParamArray vertices()) As Single
  Dim t&, aX&, aY&, sX&, sY&, i&, u%, c As Integer, d%
  Dim p() As pointXY
 
  u = UBound(vertices) - 1
  If u < 4 Or u Mod 2 <> 0 Then
    calcPolygonArea = 0
    Exit Function
  End If
  u = (u + 2) / 2
  ReDim p(1 To u + 1)
  For i = 1 To u
    p(i).X = vertices(i * 2 - 2)
    p(i).Y = vertices(i * 2 - 1)
  Next
  p(i).X = vertices(0)
  p(i).Y = vertices(1)
  For i = 1 To u
    c = InOrOut(p, i)
    Select Case c
    Case 0:
    Case -1: t = t + (p(i).X + p(i + 1).X) * (p(i).Y - p(i + 1).Y)
    Case 1:
    End Select
  Next
  calcPolygonArea = VBA.Abs(t / 2)
End Function


Function InOrOut(p() As pointXY, ByVal index As Integer) As Integer
  'k = length(p)
  'Chýìc nãng xác ðiònh viò trí cuÒa ðiêÒm (p0.X, p0.Y) týõng ðôìi
  'ðêìn ðýõÌng bao khép kín ðýõòc xác ðiònh bõÒi n ðiêÒm võìi
  'toòa ðôò p (i), p (i), i = 1, 2, ..., k
  '
  'ÐiêÒm 1 phaÒi giôìng võìi ðiêÒm k
  'Giá triò hàm ðýõòc traÒ vêÌ ():
  '1: ðiêÒm nãÌm bên trong ðýõÌng viêÌn
  '-1: ðiêÒm nãÌm ngoài ðýõÌng viêÌn
  '0: ðiêÒm nãÌm trên ðýõÌng viêÌn
'************************************************* ********************
    Dim kross As Integer, i As Integer
    Dim pp As Single, k As Integer, p0 As pointXY
    k = UBound(p)
    If index > k Or k < 1 Then
      InOrOut = -10
      Exit Function
    End If
    p0 = p(index)
    'KhõÒi taòo hàm
    InOrOut = 0
    'KhõÒi taòo kross, môòt biêìn theo doÞi
    'bãÌng bao nhiêu lâÌn bán kính thãÒng ngang
    'doÌng bãìt ðâÌu taòi (p0.X, p0.Y) và theo chiêÌu dýõng (phía bên tay phaÒi)
    'hýõìng truòc x chãòn ðýõÌng bao
    '(ÐýõÌng bao có thêÒ có các ðiÒnh võìi góc lõìn hõn 180 ðôò)
    kross = 0
    'VoÌng qua tâìt caÒ các mãòt cuÒa ðýõÌng viêÌn
    For i = 1 To k - 1
        'Nêìu caònh giýÞa ðiÒnh i và i + 1 nãÌm hoàn toàn phía trên
        'hoãòc thâìp hõn ðiêÒm (p0.X, p0.Y), boÒ qua nó (không có ðiêÒm chãòn)
        If (p(i).Y > p0.Y And p(i + 1).Y > p0.Y) Or (p(i).Y < p0.Y And p(i + 1).Y < p0.Y) Then GoTo NextItem
        'Nêìu caònh nãÌm ngang, tránh tính toán ðiêÒm chãòn bãÌng nôòi suy
        'viÌ seÞ có môòt phép chia cho sôì không
        If p(i).Y = p(i + 1).Y Then
            'Nó phaÒi ðýõòc xác ðiònh nêìu ðiêÒm (p0.X, p0.Y) nãÌm trên ðoaòn ngang này
            If (p(i).X > p0.X And p(i + 1).X > p0.X) Or (p(i).X < p0.X And p(i + 1).X < p0.X) Then GoTo NextItem
            'Nêìu không, chúng ta ðaÞ hoàn tâìt!
            Exit For
        End If
        'Tính pp, toòa ðôò cuÒa ðiêÒm mà ðoaòn nôìi võìi
        'các caònh i và i + 1 và ðýõÌng thãÒng nãÌm ngang ði qua (p0.X, p0.Y) giao nhau
        pp = p(i).X + (p0.Y - p(i).Y) * ((p(i + 1).X - p(i).X) / (p(i + 1).Y - p(i).Y))
        'Dâìu cuÒa pp-p0.X xác ðiònh viò trí cuÒa ðiêÒm chãòn so võìi (p0.X, p0.Y)
        If pp - p0.X > 0 Then
            'Ðánh chãòn õÒ bên phaÒi: bôò ðêìm gia tãng (nhýng chiÒ khi ðiêÒm ðánh chãòn không nói dôìi
            'trên ðiÒnh ðâÌu tiên, ðêÒ tránh ðêìm cùng môòt ðiêÒm chãòn hai lâÌn!)
            If p0.Y <> p(i).Y Then kross = kross + 1
        ElseIf pp - p0.X = 0 Then
            Exit For
        End If
        'Nêìu chôìt chãòn nãÌm õÒ bên trái, không thýòc hiêòn hành ðôòng nào và tiêìp tuòc
NextItem:
    Next
    'Nêìu sôì ðiêÒm giao nhau là sôì chãÞn, ðiêÒm (p0.X, p0.Y) nãÌm ngoài ðýõÌng bao
    'Nêìu nó kyÌ quãòc thiÌ nó nãÌm bên trong
    InOrOut = IIf(kross Mod 2 = 0, -1, 1)
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Trong Sheet1 đọc kỹ hướng dẫn trong TextBox để biết cách nhập dữ liệu của các đỉnh của đa giác trong cột A và B.

Có thể dùng hàm Excel - ô D1 (nhìn hình), hoặc hàm người dùng ở E1
Mã:
=dientich_dagiac(A3:B8)
Trong đó hàm người dùng
Mã:
Function dientich_dagiac(ByVal XY)
'    XY la Range co 2 cot va so dong >= 3, hoac la mang  2 chieu co 2 cot va so dong >= 3, co chi so dong cot tinh tu 1
Dim r As Long, sodong As Long, dientich As Double, dulieu
    dulieu = XY
    On Error GoTo error_
    sodong = UBound(dulieu, 1)
    If sodong < 3 Or UBound(dulieu, 2) < 2 Then GoTo error_
    On Error GoTo 0
    dientich = dulieu(1, 1) * (dulieu(2, 2) - dulieu(sodong, 2))
    For r = 2 To sodong - 1
        dientich = dientich + dulieu(r, 1) * (dulieu(r + 1, 2) - dulieu(r - 1, 2))
    Next r
    dientich = dientich + dulieu(sodong, 1) * (dulieu(1, 2) - dulieu(sodong - 1, 2))
    dientich_dagiac = Abs(dientich) / 2
    Exit Function
error_:
    dientich_dagiac = CVErr(2042)
End Function

dtdg.jpg
 

File đính kèm

  • dien tich da giac theo Gauss.xlsm
    23.5 KB · Đọc: 18
Upvote 0
Đa giác thì cả lồi cả lõm là tổng quát rồi.
Em chưa kiểm tra nhưng em nhớ là phải nhập tọa độ các đỉnh theo chiều kim đồng hồ hoặc ngược lại, không thì kết quả bị sai.
 
Upvote 0
Chính xác. Thuận thì đúng, nghịch thì chịu. :)
Mấy cái code loành tráng chúng có chỗ tét cái này.
Tôi đoán code ở bài #5 có làm chuyện ấy nhưng vì chúng chú thích lạ quá tôi không hiểu nên không dám xác định thêm. Điển hình, hàm bảo rằng nó trả về -1/0/1 nhưng trong code có chỗ trả về -10 (có lẽ do bẫy một lỗi không bao giờ xảy ra cho nên tét không thấy?).

Tôi nhớ hồi xưa có thấy qua một chương trình (phần mềm) tự xếp lại các điểm để cho ra đa giác đúng điệu. Chương trình này không cho biết thuật toán nên tôi không tìm hiểu thêm.
 
Lần chỉnh sửa cuối:
Upvote 0
Trong Sheet1 đọc kỹ hướng dẫn trong TextBox để biết cách nhập dữ liệu của các đỉnh của đa giác trong cột A và B.

Có thể dùng hàm Excel - ô D1 (nhìn hình), hoặc hàm người dùng ở E1
Mã:
=dientich_dagiac(A3:B8)
Trong đó hàm người dùng
Mã:
Function dientich_dagiac(ByVal XY)
'    XY la Range co 2 cot va so dong >= 3, hoac la mang  2 chieu co 2 cot va so dong >= 3, co chi so dong cot tinh tu 1
Dim r As Long, sodong As Long, dientich As Double, dulieu
    dulieu = XY
    On Error GoTo error_
    sodong = UBound(dulieu, 1)
    If sodong < 3 Or UBound(dulieu, 2) < 2 Then GoTo error_
    On Error GoTo 0
    dientich = dulieu(1, 1) * (dulieu(2, 2) - dulieu(sodong, 2))
    For r = 2 To sodong - 1
        dientich = dientich + dulieu(r, 1) * (dulieu(r + 1, 2) - dulieu(r - 1, 2))
    Next r
    dientich = dientich + dulieu(sodong, 1) * (dulieu(1, 2) - dulieu(sodong - 1, 2))
    dientich_dagiac = Abs(dientich) / 2
    Exit Function
error_:
    dientich_dagiac = CVErr(2042)
End Function

View attachment 272116
Em cảm ơn bác nhiều. Em dùng công thức của bác thì chuẩn rồi, công thức function đúng thì phải nhập vào array sao cho thứ tự các điểm phải theo thứ tự theo 1 vòng tròn. có cách nào sửa để nhập vào là dientich_dagiac(x1,x2,x2,y2,x3,y3,x4,y4...) không bác
 
Upvote 0
Tôi nhớ hòi xưa có thấy qua một chương trình (phần mềm) tự xếp lại các điểm để cho ra đa giác đúng điệu. Chương trình này không cho biết thuật toán nên tôi không tìm hiểu thêm.
Do trước cũng lõm bõm mò mấy vụ này, nên chia sẻ thêm luôn ạ (Chỉ đúng với đa giác lồi).
Đại ý rằng trong tập hợp các điểm đang xét.
+ Chọn lấy 1 điểm (Điểm gốc tạm), xét các điểm còn lại và loại ra khỏi tập cho đến hết, điều kiện rằng sẽ tìm ra đỉnh tiếp theo (ngược chiều với kim đồng hồ) thỏa điều kiện tất cả các điểm còn lại sẽ nằm bên trái so với đoạn thẳng (đường thẳng) do điểm đang đang xét và điểm gốc tạo ra.
+ Lặp lại bước trên đến hến với điểm gốc tạm thay thế bằng điểm mới tìm thấy.

1644831877284.png

** đoạn trả về trả về -1/0/1 chắc là để kiểm tra việc này.
 
Lần chỉnh sửa cuối:
Upvote 0
Đúng như vài người góp ý là phải đưa ra một dãy thứ tự các đỉnh liền kề nhau và vấn đề này sẽ được giải quyết rất ĐƠN GIẢN nếu ta tìm được 4 điểm cực bao gồm:
  1. Cực trái
  2. Cực trên
  3. Cực phải
  4. Cực dưới.
Và việc xác định 4 cực này cũng chả cực đâu khi chỉ cần tìm MAX, MIN tọa độ x-y mà thôi (tới đây coi như đã giải quyết xong 50% vấn đề --=0 ). Khi tìm ra được 4 điểm cực này rồi thì cái thứ tự lần lượt các đỉnh cũng trở nên không khó lắm (đoạn này mới thách thức tý khả năng của bạn nè)

Đây là thành quả sau hơn 30 phút tập trung suy nghĩ. Tôi thì chưa đủ trỉnh ở cả lập trình và giải thuật nên sẽ chẳng viết nổi dòng code nào minh họa đâu.

Bài toán này sẽ giúp nhận định ra là bạn thực sử giỏi giải thuật hay là bạn mới chỉ giỏi về kỹ thuật code thuần túy. Một cơ hội tốt để các bạn hiểu rõ khả năng của minh đang ở đâu thì đừng nên bỏ qua nhé. Và nếu anh chị nào nếu tìm được ý tưởng giải quyết từ những gợi ý của tôi thì đừng ngại để lại tương tác vào comment của tôi nha --=0
 
Lần chỉnh sửa cuối:
Upvote 0
Em cảm ơn bác nhiều. Em dùng công thức của bác thì chuẩn rồi, công thức function đúng thì phải nhập vào array sao cho thứ tự các điểm phải theo thứ tự theo 1 vòng tròn. có cách nào sửa để nhập vào là dientich_dagiac(x1,x2,x2,y2,x3,y3,x4,y4...) không bác
Cỡ trình độ người muốn giải toán loại này mà không biết viết wrapper thì đáng thất vọng thật.
Nói cách khác, người muốn vọc ba cái mớ cao cấp thì tít nhất phải đạt một trình độ code kha khá, và chỉ hỏi những chỗ bí. Chữ chả lẽ mõi lần vọc lại mỗi lần xin code từ a đến z.

Viết thêm một cái hàm DienTichDaGiac(ParamArray xy)

Bên trong hàm, đặt một Array có số cột là 2 và số dòng là n. Tức số tham số của (x1,x2,x2,y2,x3,y3,x4,y4...) chia cho 2.
cứ tuần tự nạp
a(1, 1) = tham1; a(1,2) = tham2
a(2, 1) = tham3; a(2, 2) = tham3
...

Xong gọi hàm dientich_dagiac(a)
Hết
 
Upvote 0
Bài này chắc không chỉ dừng lại ở tính diện tích. :wiggle:
Em đoán còn khoảng 4-5 bước tiếp theo sử dụng hình học nữa.
 
Upvote 0
Bài này chắc không chỉ dừng lại ở tính diện tích. :wiggle:
Em đoán còn khoảng 4-5 bước tiếp theo sử dụng hình học nữa.
Thiệt ra tôi chỉ mách cho cách viết VBA thôi.
Chứ nếu thớt chịu khó đọc code bài #6 một chút thì đã biết nó có thể nạp theo mảng thay vì range.

Công thức nạp mảng:
=dientich_dagiac( Choose( { 1, 2; 3, 4; 5, 6; ...; 2n-1, 2n }, X1, Y1, X2, Y2, X3, Y3, ..., Xn, Yn ) )
Chú ý: bên trong hàm Choose, giữa 2i-1 (X) và 2i (Y) là dấu phẩy, giữa 2i-2 và 2i-1 là dấu chấm phẩy.
Nói cách khác cho người không rành toán số thì cứ bắt đầu là 1, dấu phẩy tồi sang 2, dấu chấm phẩy rồi sang 3,... Cứ thế cho đến khi số cuối là 2 lần số đỉnh đa giác. Sau hàm choose thì bắt đầy nạp, X1, Y1,...
 
Upvote 0
Do trước cũng lõm bõm mò mấy vụ này, nên chia sẻ thêm luôn ạ (Chỉ đúng với đa giác lồi).
Đại ý rằng trong tập hợp các điểm đang xét.
+ Chọn lấy 1 điểm (Điểm gốc tạm), xét các điểm còn lại và loại ra khỏi tập cho đến hết, điều kiện rằng sẽ tìm ra đỉnh tiếp theo (ngược chiều với kim đồng hồ) thỏa điều kiện tất cả các điểm còn lại sẽ nằm bên trái so với đoạn thẳng (đường thẳng) do điểm đang đang xét và điểm gốc tạo ra.
+ Lặp lại bước trên đến hến với điểm gốc tạm thay thế bằng điểm mới tìm thấy.
Ý tưởng giải quyết này khác ý tưởng của tôi nhưng khi hiểu lơ mơ ra thì tôi cũng xin có vài ý kiến.

Ban đầu thì tôi không hiểu ý bạn lắm vì cái tiêu chí "ngược chiều với kim đồng hồ". Tuy nhiên khi ngẫm nghĩ lại thì tôi đoán cách bạn xác định đỉnh liền kế là đỉnh có khoảng cách gần nhất với đỉnh làm mốc (loại trừ cái đỉnh liền kế trước với mốc đã biết). Như vậy thì tiêu chí "ngược chiều với kim đồng hồ" hay nói đúng hơn là "ngược chiều quay..." liệu có cần xét tới nữa không nhỉ? Mình hoàn toàn có thể tìm theo chiều thuận hoặc chiều xuôi thì vẫn ổn mà? Nếu thêm tiêu chí "tìm ngược theo chiều quay của kim đồng hồ" có khi làm phương thức lại phức tạp thêm. Không biết tôi hiểu đúng ý tưởng của bạn chưa nhỉ?
 
Lần chỉnh sửa cuối:
Upvote 0
Nói về cách sử dụng hàm dientich_dagiac ở bài #6:

Ở bài #15 tôi đã mách cho cách gọi hàm này và nạp tham số trực tiếp theo dãy số thay vì dùng số liệu trong range.

Tôi viết hàm wapper sau đây để cho quý vị nào muốn học cách viét hàm wapper:

Hàm phụ, đổi một dãy số, hoặc array 1 cột thành array 2 cột.

Function DaySoRaMang(a As Variant) As Variant
Const SOCOT = 2
Dim b()
Dim iDong As Long, iCot As Long
Dim e As Variant
ReDim b(1 To (UBound(a) - LBound(a) + 1) \ SOCOT, 1 To SOCOT)
iCot = SOCOT ' just to trigger next iDong
iDong = 0
For Each e In a
iCot = iCot + 1
If iCot > SOCOT Then ' all columns filled, move to next row
iCot = 1
iDong = iDong + 1
End If
b(iDong, iCot) = e
Next e
DaySoRaMang = b
End Function

Hàm wrapper:

Function DienTichDaGiac(ParamArray a())
DienTichDaGiac = dientich_dagiac(DaySoRaMang(a))
End Function

Chú ý: hàm wrapper được viết theo 2 nguyên tắc
1. "If it aint broke, dont break it"
Nói cách khác, hàm dientich_dagiac chạy tốt rồi thì để yên đấy. Cần thêm cái gì thì viết hàm wrapper gọi nó.
2. thường thì người ta viết hàm wrapper là để gọi từ một hàm khác.
Hàm A chạy tốt, hàm B cũng chạy tốt. Bây giờ thêm gì đó khiến B cần gọi A. Nhưng nếu chỉnh code B để gọi A thì hơi nhiều, khó kiểm soát và debug code. Hàm wrapper hay hàm trung gian giúp gian đoạn chuẩn bị dữ liệu để gọi A là giải pháp rất hiệu nghiệm. (tôi chỉ nói hiệu nghiệm chứ không nói tốc độ, hay giảm dòng code theo tiêu chí GPE gì cả. Ai cũng biết gọi thêm một hàm là chậm vài phần ngàn hay triệu giây, không cần bàn cãi)


Chú thích:
Vì lý do cá nhân, tôi tránh phê bình code ở bài #5.
Chỉ muốn nhắc cho bạn nào có ý định sử dụng code ấy: code ấy hoạt động trên căn bản số thực, nhưng không hiểu vì sao lại đổi các tham số nhập vào thành số nguyên. Vì vậy:
calcPolygonArea(1,1,2,3,1,4,1,0) = 1.5
calcPolygonArea(1,1,2,3,1,4.4,1,0) = 1.5 --> 4.4 bị đổi thành4
calcPolygonArea(1,1,2,3,1,4.6,1,0) = 2 --> 4.6 bị đổi thành 5
calcPolygonArea(1,1,2,3,1,5,1,0) = 2
 
Lần chỉnh sửa cuối:
Upvote 0
STT X_i Y_i
A 0.00 200.00
B 600.00 400.00
C 800.00 800.00
D 400.00 900.00
E 200.00 1400.00
F -200.00 1200.00
G -300.00 600.00
A 0.00 200.00


Hay làm cái ví dụ cho vui ạ.
Dựa trên thông tin tọa độ đỉnh của bảng nêu trên
1. Tính diện tích đa giác.
2. Tìm trọng tâm của đa giác trên.
 
Lần chỉnh sửa cuối:
Upvote 0
STTXY
A0200
B600400
C800800
D400900
E2001400
F-2001200
G-300600
A0200

Hay làm cái ví dụ cho vui ạ.
Dựa trên thông tin tọa độ đỉnh của bảng nêu trên
1. Tính diện tích đa giác.
2. Tìm trọng tâm của đa giác trên.
Trọng tâm có lý thuyết và công thức của nó.
Trọng tâm của một hình là phép tính tỷ lượng các vector của các hình nhỏ thành phần.
Để tính bài này, ta có thể chia hình thành các tam giác ABC, ACD, ADE,...
Tính xy của mỗi trọng tâm xc = (x1+x2+x3)/3, yc = (y1+y2+y3)/3. Và diện tích w mỗi tam giác. Có thẻ gọi các hàm tính diện tích ở trên, hay tự dùng công thức tam giác A = 0.5*abs(x1(y2-y3) + x2(y3-y1)+x3(y1-y2))
X trọng tâm chính = (w1x1 + w2x2 + .... + wnxn) / (w1 + w2 + ... + wn)
Y trọng tâm chính = (w1y1 + w2y2 + .... + wnyn) / (w1 + w2 + ... + wn)
Bài này tính bằng code là một bài tập thú vị cho lập trình nói chung (sẽ giải thích tại sao "thú vị" sau, gợi ý làm mất hứng). VBA thì tôi không biết có lợi không vì nó không hẳn thực dụng.
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom