2014-10-11 80 views
0

我有一個程序,在VS C++的工作,不與G ++工作。這裏是代碼:不同的結果VS C++和GNU G ++

#define _USE_MATH_DEFINES 

#include <cmath> 
#include <iostream> 
#include <vector> 
#include <cstdio> 
#include <algorithm> 
#include <set> 

#define EP 1e-10 

using namespace std; 

typedef pair<long long, long long> ii; 
typedef pair<bool, int> bi; 
typedef vector<ii> vii; 

// Returns the orientation of three points in 2D space 
int orient2D(ii pt0, ii pt1, ii pt2) 
{ 
    long long result = (pt1.first - pt0.first)*(pt2.second - pt0.second) 
     - (pt1.second - pt0.second)*(pt2.first - pt0.first); 
    return result == 0 ? 0 : result < 0 ? -1 : 1; 
} 

// Returns the angle derived from law of cosines center-pt1-pt2. 
// Defined to be negative if pt2 is to the right of segment pt1 to center 
double angle(ii center, ii pt1, ii pt2) 
{ 
    double aS = pow(center.first - pt1.first, 2) + pow(center.second - pt1.second, 2); 
    double bS = pow(pt2.first - pt1.first, 2) + pow(pt2.second - pt1.second, 2); 
    double cS = pow(center.first - pt2.first, 2) + pow(center.second - pt2.second, 2); 

/* long long aS = (center.first - pt1.first)*(center.first - pt1.first) + (center.second - pt1.second)*(center.second - pt1.second); 
    long long bS = (pt2.first - pt1.first)*(pt2.first - pt1.first) + (pt2.second - pt1.second)*(pt2.second - pt1.second); 
    long long cS = (center.first - pt2.first)*(center.first - pt2.first) + (center.second - pt2.second)*(center.second - pt2.second);*/ 

    int sign = orient2D(pt1, center, pt2); 

    return sign == 0 ? 0 : sign * acos((aS + bS - cS)/((sqrt(aS) * sqrt(bS) * 2))); 
} 

// Computes the average point of the set of points 
ii centroid(vii &pts) 
{ 
    ii center(0, 0); 
    for (int i = 0; i < pts.size(); ++i) 
    { 
     center.first += pts[i].first; 
     center.second += pts[i].second; 
    } 

    center.first /= pts.size(); 
    center.second /= pts.size(); 

    return center; 
} 

// Uses monotone chain to convert a set of points into a convex hull, ordered counter-clockwise 
vii convexHull(vii &pts) 
{ 
    sort(pts.begin(), pts.end()); 
    vii up, dn; 
    for (int i = 0; i < pts.size(); ++i) 
    { 
     while (up.size() > 1 && orient2D(up[up.size()-2], up[up.size()-1], pts[i]) >= 0) 
      up.pop_back(); 
     while (dn.size() > 1 && orient2D(dn[dn.size()-2], dn[dn.size()-1], pts[i]) <= 0) 
      dn.pop_back(); 

     up.push_back(pts[i]); 
     dn.push_back(pts[i]); 
    } 

    for (int i = up.size()-2; i > 0; --i) 
    { 
     dn.push_back(up[i]); 
    } 

    return dn; 
} 

// Tests if a point is critical on the polygon, i.e. if angle center-qpt-polygon[i] 
// is larger (smaller) than center-qpt-polygon[i-1] and center-qpt-polygon[i+1]. 
// This is true iff qpt-polygon[i]-polygon[i+1] and qpt-polygon[i]-polygon[i-1] 
// are both left turns (min) or right turns (max) 
bool isCritical(vii &polygon, bool mx, int i, ii qpt, ii center) 
{ 
    int ip1 = (i + 1) % polygon.size(); 
    int im1 = (i + polygon.size() - 1) % polygon.size(); 

    int p1sign = orient2D(qpt, polygon[i], polygon[ip1]); 
    int m1sign = orient2D(qpt, polygon[i], polygon[im1]); 

    if (p1sign == 0 && m1sign == 0) 
    { 
     return false; 
    } 

    if (mx) 
    { 
     return p1sign <= 0 && m1sign <= 0; 
    } 
    else 
    { 
     return p1sign >= 0 && m1sign >= 0; 
    } 
} 

// Conducts modified binary search on the polygon to find tangent lines in O(log n) time. 
// This is equivalent to finding a max or min in a "parabola" that is rotated and discrete. 
// Vanilla binary search does not work and neither does vanilla ternary search. However, using 
// the fact that there is only a single max and min, we can use the slopes of the points at start 
// and mid, as well as their values when compared to each other, to determine if the max or min is 
// in the left or right section 
bi find_tangent(vii &polygon, bool mx, ii qpt, int start, int end, ii center) 
{ 
    // When query is small enough, iterate the points. This avoids more complicated code dealing with the cases not possible as 
    // long as left and right are at least one point apart. This does not affect the asymptotic runtime. 
    if (end - start <= 4) 
    { 
     for (int i = start; i < end; ++i) 
     { 
      if (isCritical(polygon, mx, i, qpt, center)) 
      { 
       return bi(true, i); 
      } 
     } 

     return bi(false, -1); 
    } 

    int mid = (start + end)/2; 

    // use modulo to wrap around the polygon 
    int startm1 = (start + polygon.size() - 1) % polygon.size(); 
    int midm1 = (mid + polygon.size() - 1) % polygon.size(); 

    // left and right angles 
    double startA = angle(center, qpt, polygon[start]); 
    double midA = angle(center, qpt, polygon[mid]); 

    // minus 1 angles, to determine slope 
    double startm1A = angle(center, qpt, polygon[startm1]); 
    double midm1A = angle(center, qpt, polygon[midm1]); 

    int startSign = abs(startm1A - startA) < EP ? 0 : (startm1A < startA ? 1 : -1); 
    int midSign = abs(midm1A - midA) < EP ? 0 : (midm1A < midA ? 1 : -1); 

    bool left = true; 
    // naively 27 cases: left and left angles can be <, ==, or >, 
    // slopes can be -, 0, or +, and each left and left has slopes, 
    // 3 * 3 * 3 = 27. Some cases are impossible, so here are the remaining 18. 
    if (abs(startA - midA) < EP) 
    { 
     if (startSign == -1) 
     { 
      left = !mx; 
     } 
     else 
     { 
      left = mx; 
     } 
    } 
    else if (startA < midA) 
    { 
     if (startSign == 1) 
     { 
      if (midSign == 1) 
      { 
       left = false; 
      } 
      else if (midSign == -1) 
      { 
       left = mx; 
      } 
      else 
      { 
       left = false; 
      } 
     } 
     else if (startSign == -1) 
     { 
      if (midSign == -1) 
      { 
       left = true; 
      } 
      else if (midSign == 1) 
      { 
       left = !mx; 
      } 
      else 
      { 
       left = true; 
      } 
     } 
     else 
     { 
      if (midSign == -1) 
      { 
       left = false; 
      } 
      else 
      { 
       left = true; 
      } 
     } 
    } 
    else 
    { 
     if (startSign == 1) 
     { 
      if (midSign == 1) 
      { 
       left = true; 
      } 
      else if (midSign == -1) 
      { 
       left = mx; 
      } 
      else 
      { 
       left = true; 
      } 
     } 
     else if (startSign == -1) 
     { 
      if (midSign == -1) 
      { 
       left = false; 
      } 
      else if (midSign == 1) 
      { 
       left = !mx; 
      } 
      else 
      { 
       left = false; 
      } 
     } 
     else 
     { 
      if (midSign == 1) 
      { 
       left = true; 
      } 
      else 
      { 
       left = false; 
      } 
     } 
    } 

    if (left) 
    { 
     return find_tangent(polygon, mx, qpt, start, mid+1, center); 
    } 
    else 
    { 
     return find_tangent(polygon, mx, qpt, mid, end, center); 
    } 
} 

int main(){ 
    int n, m; 
    cin >> n >> m; 

    vii rawPoints(n); 
    for (int i = 0; i < n; ++i) 
    { 
     cin >> rawPoints[i].first >> rawPoints[i].second; 
    } 

    vii polygon = convexHull(rawPoints); 
    set<ii> points(polygon.begin(), polygon.end()); 
    ii center = centroid(polygon); 

    for (int i = 0; i < m; ++i) 
    { 
     ii pt; 
     cin >> pt.first >> pt.second; 

     bi top = find_tangent(polygon, true, pt, 0, polygon.size(), center); 
     bi bot = find_tangent(polygon, false, pt, 0, polygon.size(), center); 

     // a query point is inside if it is collinear with its max (top) and min (bot) angled points, it is a polygon point, or if none of the points are critical 
     if (!top.first || orient2D(polygon[top.second], pt, polygon[bot.second]) == 0 || points.count(pt)) 
     { 
      cout << "INSIDE" << endl; 
     } 
     else 
     { 
      cout << polygon[top.second].first << " " << polygon[top.second].second << " " << polygon[bot.second].first << " " << polygon[bot.second].second << endl; 
     } 
    } 
} 

我懷疑angle函數有什麼問題。我已經縮小到或者find_tangent。當我在angle函數中從double切換到long long時,我也在g ++中看到不同的結果。 double結果更接近正確,但我不明白爲什麼它應該有任何不同。我輸入的值很小,沒有溢出/舍入應該引起問題。當我分配給雙精度時,我也看到了在做pow(x, 2)x*x時的差異。我不明白爲什麼這會有所作爲。

任何幫助,將不勝感激!

編輯:這裏是輸入文件:https://github.com/brycesandlund/Coursework/blob/master/Java/PrintPoints/points.txt

這裏是正確的結果: https://github.com/brycesandlund/Coursework/blob/master/CompGeo/CompGeo/correct.txt

這裏是不正確的結果: https://github.com/brycesandlund/Coursework/blob/master/CompGeo/CompGeo/fast.txt

+0

預期結果是什麼,實際結果是什麼? – 2014-10-11 21:17:52

+0

添加了輸入,正確和不正確的文件@ChristianHackl。 – 2014-10-11 21:35:00

+0

將其縮小到調用其他函數並以遞歸調用結束的方法並不真正縮小它的範圍。是否有可能將其減少爲顯示行爲的簡單示例。 – ChiefTwoPencils 2014-10-11 21:36:14

回答

0

的問題是與這一段代碼:

// Computes the average point of the set of points 
ii centroid(vii &pts) 
{ 
    ii center(0LL, 0LL); 
    for (int i = 0; i < pts.size(); ++i) 
    { 
     center.first += pts[i].first; 
     center.second += pts[i].second; 
    } 

    center.first /= pts.size();  //right here!! 
    center.second /= pts.size(); 

    return center; 
} 

我不知道爲什麼,但g ++被取負center.first,使之成爲一個積極的,由無符號整數pts.size分割時溢出long long。通過將這些語句轉換爲:

center.first /= (long long)pts.size(); 
center.second /= (long long)pts.size(); 

g ++和VS C++的輸出匹配。

+3

我想我明白了。因爲一方是無符號的,另一方也轉換爲無符號。在32位的情況下,它工作正常,因爲'size_t'是'uint32_t',但在64位中,'size_t'與'uint64_t'(或'long long')相同,所以'pts.size ()'在分割之前將左邊的大小轉換爲'unsigned long long'(aka'uint64_t')。 – 2014-10-11 23:33:46

+0

@MatsPetersson有趣的問題是,爲什麼不MSVC做同樣的 - MSVC中的錯誤或標準是寬鬆的? – user4815162342 2014-10-12 07:21:55