มีวิธีใดที่เร็วกว่าในการตรวจสอบว่ารายการในรายการเทียบเท่าหรือไม่


9

นี่ฉันมีจำนวนเต็ม1:7สำหรับสี่พาร์ทิชันที่แตกต่างกันกล่าวคือ {1}, {2,3,4}, {5,6} และ {7} list(1,c(2,3,4),c(5,6),7)และพาร์ทิชันเหล่านั้นจะถูกเขียนในรายการคือ ฉันถือว่าพาร์ติชั่นเป็นชุด, ดังนั้นการเปลี่ยนแปลงองค์ประกอบที่แตกต่างกันภายในพาร์ติชั่นหนึ่งควรถูกจดจำเป็นพาร์ติชั่นเดียวกัน. ตัวอย่างเช่นlist(1,c(2,3,4),c(5,6),7)และlist(7,1,c(2,3,4),c(6,5))เทียบเท่า

โปรดทราบว่าไม่มีการทำซ้ำสำหรับองค์ประกอบในรายการเช่นไม่ใช่list(c(1,2),c(2,1),c(1,2))เนื่องจากปัญหานี้กำลังพูดถึงพาร์ติชันพิเศษเหนือทั้งชุด

ฉันแสดงรายการการเรียงลำดับที่แตกต่างกันบางรายการลงในรายการ lstด้านล่าง

lst <- list(list(1,c(2,3,4),c(5,6),7),
            list(c(2,3,4),1,7,c(5,6)),
            list(1,c(2,3,4),7,c(6,5)),
            list(7,1,c(3,2,4),c(5,6)))

และสิ่งที่ฉันต้องการทำคือการตรวจสอบว่าพีชคณิตทั้งหมดมีความเท่าเทียมกัน TRUEถ้าใช่แล้วเราจะได้รับผล

สิ่งที่ฉันทำจนถึงตอนนี้คือการจัดเรียงองค์ประกอบภายในแต่ละพาร์ติชันและใช้setdiff()กับinterset()และunion()ตัดสินมัน (ดูรหัสของฉันด้านล่าง)

s <- Map(function(v) Map(sort,v),lst)
equivalent <- length(setdiff(Reduce(union,s),Reduce(intersect,s),))==0

อย่างไรก็ตามฉันคิดว่าวิธีนี้จะช้าเมื่อใดก็ตามที่ขนาดพาร์ติชันเพิ่มขึ้น มีวิธีเร็วกว่าที่จะทำหรือไม่ ขอบคุณล่วงหน้า!

  • กรณีทดสอบบางอย่าง (ข้อมูลขนาดเล็ก)
# should return `TRUE`
lst1 <- list(list(1,c(2,3,4),c(5,6)),
            list(c(2,3,4),1,c(5,6)),
            list(1,c(2,3,4),c(6,5)))

# should return `TRUE`
lst2 <- list(list(1:2, 3:4), list(3:4, 1:2))

# should return `FALSE`
lst3 <- list(list(1,c(2,3,4),c(5,6)), list(c(2,3,4),1,c(5,6)), list(1,c(2,3,5),c(6,4)))

1
ฉันเดาว่าคุณสามารถหลีกเลี่ยงการMapโทรหลายสาย
akrun

1
ผมขอแนะนำให้เพิ่มไม่กี่กรณีทดสอบเพิ่มเติมคำถามของคุณเป็นหนึ่งเดียวกับพาร์ทิชันที่มีขนาดเท่ากัน, lst_equal = list(list(1:2, 3:4), list(3:4, 1:2))และยังเป็นหนึ่งที่ผลที่ควรจะFALSEบางทีlst_false <- list(list(1,c(2,3,4),c(5,6)), list(c(2,3,4),1,c(5,6)), list(1,c(2,3,5),c(6,4)))
เกรโทมัส

3
ผมขออยากแนะนำให้มีตัวอย่างเล็ก ๆ หลาย - FALSEรวมทั้งบางส่วนที่ผลที่คาดหวังคือ ด้วยวิธีนี้เมื่อคำตอบใช้งานได้ในบางกรณี แต่ไม่ใช่ทั้งหมดกรณีทดสอบมันง่ายที่จะวินิจฉัยว่าทำไม เมื่อมีเพียงตัวอย่างเดียวคุณจะเสียความแตกต่างเล็กน้อยในผลการทดสอบ นอกจากนี้ยังเป็นการดีที่จะเพิ่มตัวอย่างใหม่แทนที่จะเปลี่ยนตัวอย่างที่มีอยู่ภายใต้คนที่ทำงานกับพวกเขาแล้ว
Gregor Thomas

1
ฉันต้องการเพิ่มความคิดเห็นที่คำอธิบายของคุณทำให้ฉันคิดว่าคุณคาดหวังผลลัพธ์ที่จะเป็นจริงคุณแค่ยืนยันมัน หากไม่ใช่ในกรณีนี้ (เช่นหากคุณคิดว่าคุณจะได้รับ FALSE จำนวนมาก) และโดยเฉพาะอย่างยิ่งหากความยาวของความยาวlstอาจยาวนานคุณอาจเพิ่มประสิทธิภาพด้วยวิธีการอื่น เช่นการตรวจสอบครั้งแรกที่length(unique(lengths(lst))) == 1จะกลับมาอย่างรวดเร็วมากFALSEหากรายการด้านในมีจำนวนองค์ประกอบที่ไม่ถูกต้อง ....
Gregor Thomas

1
หากผ่านคุณอาจต้องไปทีละรายการlstเปรียบเทียบlst[[i]]กับlst[[1]]และวิธีที่คุณสามารถหยุดทันทีที่คุณพบความไม่ตรงกันแทนที่จะทำการเปรียบเทียบทั้งหมด หากlstเป็นเวลานานและFALSEเป็นเรื่องปกตินี่อาจเป็นการเพิ่มประสิทธิภาพอย่างมาก แต่ก็อาจไม่คุ้มกับที่อื่น
Gregor Thomas

คำตอบ:


6

โพสต์เกี่ยวกับRและตัวแปรใด ๆได้อย่างรวดเร็วไม่สมบูรณ์โดยไม่ต้องมีการแก้ปัญหาที่มีrcpp

เพื่อเพิ่มประสิทธิภาพสูงสุดการเลือกโครงสร้างข้อมูลที่ถูกต้องจะมีความสำคัญสูงสุด โครงสร้างข้อมูลของเราต้องเก็บค่าที่ไม่ซ้ำกันและยังมีการแทรก / การเข้าถึงที่รวดเร็ว นี่คือสิ่งที่std :: unordered_setคาดเดา เราจำเป็นต้องพิจารณาว่าเราจะสามารถจำแนกเอกลักษณ์ที่ไม่ได้vectorเรียงลำดับintegersอย่างไร

ใส่ทฤษฎีบทพื้นฐานของเลขคณิต

เขตการค้าเสรีระบุว่าทุกหมายเลขสามารถแสดงได้ไม่ซ้ำกัน (ขึ้นอยู่กับคำสั่งของปัจจัย) โดยผลผลิตของจำนวนเฉพาะ

นี่คือตัวอย่างที่แสดงให้เห็นว่าเราสามารถใช้ FTA เพื่อถอดรหัสอย่างรวดเร็วได้อย่างไรหากเวกเตอร์สองตัวมีค่าเทียบเท่ากับการสั่งซื้อ (หมายเหตุPด้านล่างนี้คือรายการหมายเลขช่วงเวลา ... (2, 3, 5, 7, 11, etc.):

                   Maps to                    Maps to              product
vec1 = (1, 2, 7)    -->>    P[1], P[2], P[7]   --->>   2,  3, 17     -->>   102
vec2 = (7, 3, 1)    -->>    P[7], P[3], P[1]   --->>  17,  5,  2     -->>   170
vec3 = (2, 7, 1)    -->>    P[2], P[7], P[1]   --->>   3, 17,  2     -->>   102

จากนี้เราจะเห็นว่าvec1และvec3แมปได้อย่างถูกต้องกับหมายเลขเดียวกันในขณะที่vec2แมปได้ถูกแมปกับค่าที่แตกต่างกัน

เนื่องจากเวกเตอร์ที่แท้จริงของเราอาจมีจำนวนเต็มน้อยกว่าหนึ่งร้อยน้อยกว่า 1,000 การใช้เขตการค้าเสรีจะทำให้จำนวนมาก เราสามารถแก้ไขได้โดยการใช้ประโยชน์จากกฎผลิตภัณฑ์ลอการิทึม:

log b (xy) = log b (x) + log b (y)

ด้วยสิ่งนี้เมื่อเราจัดการเราจะสามารถจัดการกับตัวอย่างตัวเลขที่มีขนาดใหญ่กว่าได้มากขึ้น (สิ่งนี้จะเริ่มแย่ลงสำหรับตัวอย่างที่มีขนาดใหญ่มาก)

อันดับแรกเราต้องการตัวสร้างจำนวนเฉพาะอย่างง่าย (NB เรากำลังสร้างบันทึกของจำนวนเฉพาะแต่ละตัว)

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::plugins(cpp11)]]

void getNPrimes(std::vector<double> &logPrimes) {

    const int n = logPrimes.size();
    const int limit = static_cast<int>(2.0 * static_cast<double>(n) * std::log(n));
    std::vector<bool> sieve(limit + 1, true);

    int lastP = 3;
    const int fsqr = std::sqrt(static_cast<double>(limit));

    while (lastP <= fsqr) {
        for (int j = lastP * lastP; j <= limit; j += 2 * lastP)
            sieve[j] = false;

        int ind = 2;

        for (int k = lastP + 2; !sieve[k]; k += 2)
            ind += 2;

        lastP += ind;
    }

    logPrimes[0] = std::log(2.0);

    for (int i = 3, j = 1; i <= limit && j < n; i += 2)
        if (sieve[i])
            logPrimes[j++] = std::log(static_cast<double>(i));
}

และนี่คือการดำเนินการหลัก:

// [[Rcpp::export]]
bool f_Rcpp_Hash(List x) {

    List tempLst = x[0];
    const int n = tempLst.length();
    int myMax = 0;

    // Find the max so we know how many primes to generate
    for (int i = 0; i < n; ++i) {
        IntegerVector v = tempLst[i];
        const int tempMax = *std::max_element(v.cbegin(), v.cend());

        if (tempMax > myMax)
            myMax = tempMax;
    }

    std::vector<double> logPrimes(myMax + 1, 0.0);
    getNPrimes(logPrimes);
    double sumMax = 0.0;

    for (int i = 0; i < n; ++i) {
        IntegerVector v = tempLst[i];
        double mySum = 0.0;

        for (auto j: v)
            mySum += logPrimes[j];

        if (mySum > sumMax)
            sumMax = mySum;
    }

    // Since all of the sums will be double values and we want to
    // ensure that they are compared with scrutiny, we multiply
    // each sum by a very large integer to bring the decimals to
    // the right of the zero and then convert them to an integer.
    // E.g. Using the example above v1 = (1, 2, 7) & v2 = (7, 3, 1)
    //              
    //    sum of log of primes for v1 = log(2) + log(3) + log(17)
    //                               ~= 4.62497281328427
    //
    //    sum of log of primes for v2 = log(17) + log(5) + log(2)
    //                               ~= 5.13579843705026
    //    
    //    multiplier = floor(.Machine$integer.max / 5.13579843705026)
    //    [1] 418140173
    //    
    // Now, we multiply each sum and convert to an integer
    //    
    //    as.integer(4.62497281328427 * 418140173)
    //    [1] 1933886932    <<--   This is the key for v1
    //
    //    as.integer(5.13579843705026 * 418140173)
    //    [1] 2147483646    <<--   This is the key for v2

    const uint64_t multiplier = std::numeric_limits<int>::max() / sumMax;
    std::unordered_set<uint64_t> canon;
    canon.reserve(n);

    for (int i = 0; i < n; ++i) {
        IntegerVector v = tempLst[i];
        double mySum = 0.0;

        for (auto j: v)
            mySum += logPrimes[j];

        canon.insert(static_cast<uint64_t>(multiplier * mySum));
    }

    const auto myEnd = canon.end();

    for (auto it = x.begin() + 1; it != x.end(); ++it) {
        List tempLst = *it;

        if (tempLst.length() != n)
            return false;

        for (int j = 0; j < n; ++j) {
            IntegerVector v = tempLst[j];
            double mySum = 0.0;

            for (auto k: v)
                mySum += logPrimes[k];

            const uint64_t key = static_cast<uint64_t>(multiplier * mySum);

            if (canon.find(key) == myEnd)
                return false;
        }
    }

    return true;
}

นี่คือผลลัพธ์เมื่อนำไปใช้กับlst1, lst2, lst3, & lst (the large one)@GKi ที่มอบให้

f_Rcpp_Hash(lst)
[1] TRUE

f_Rcpp_Hash(lst1)
[1] TRUE

f_Rcpp_Hash(lst2)
[1] FALSE

f_Rcpp_Hash(lst3)
[1] FALSE

และนี่คือมาตรฐานบางอย่างเกี่ยวกับการตั้งค่าพารามิเตอร์unitsrelative

microbenchmark(check = 'equal', times = 10
               , unit = "relative"
               , f_ThomsIsCoding(lst3)
               , f_chinsoon12(lst3)
               , f_GKi_6a(lst3)
               , f_GKi_6b(lst3)
               , f_Rcpp_Hash(lst3))
Unit: relative
                 expr       min        lq      mean    median        uq       max neval
f_ThomsIsCoding(lst3) 84.882393 63.541468 55.741646 57.894564 56.732118 33.142979    10
   f_chinsoon12(lst3) 31.984571 24.320220 22.148787 22.393368 23.599284 15.211029    10
       f_GKi_6a(lst3)  7.207269  5.978577  5.431342  5.761809  5.852944  3.439283    10
       f_GKi_6b(lst3)  7.399280  5.751190  6.350720  5.484894  5.893290  8.035091    10
    f_Rcpp_Hash(lst3)  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000    10


microbenchmark(check = 'equal', times = 10
               , unit = "relative"
               , f_ThomsIsCoding(lst)
               , f_chinsoon12(lst)
               , f_GKi_6a(lst)
               , f_GKi_6b(lst)
               , f_Rcpp_Hash(lst))
Unit: relative
                expr        min         lq       mean     median        uq       max neval
f_ThomsIsCoding(lst) 199.776328 202.318938 142.909407 209.422530 91.753335 85.090838    10
   f_chinsoon12(lst)   9.542780   8.983248   6.755171   9.766027  4.903246  3.834358    10
       f_GKi_6a(lst)   3.169508   3.158366   2.555443   3.731292  1.902140  1.649982    10
       f_GKi_6b(lst)   2.992992   2.943981   2.019393   3.046393  1.315166  1.069585    10
    f_Rcpp_Hash(lst)   1.000000   1.000000   1.000000   1.000000  1.000000  1.000000    10

เร็วกว่าโซลูชันที่เร็วที่สุดประมาณ3 เท่าแต่เป็นตัวอย่างที่ใหญ่กว่า

สิ่งนี้หมายความว่า?

สำหรับฉันผลลัพธ์นี้พูดถึงปริมาณความงามและประสิทธิภาพของbase Rที่แสดงโดย @GKi, @ chinsoon12, @Gregor, @ThomasIsCoding และอีกมากมาย เราเขียนเฉพาะเจาะจงมากC++ถึง100 บรรทัดเพื่อให้ได้ความเร็วปานกลาง เพื่อความเป็นธรรมการbase Rแก้ปัญหาการโทรไปยังโค้ดที่คอมไพล์แล้วส่วนใหญ่จะใช้ตารางแฮชตามที่เราทำข้างต้น


1
@ThomasIsCoding ฉันรู้สึกเป็นเกียรติที่คุณเลือกคำตอบของฉัน แต่ฉันเชื่อโดยสุจริตว่าคำตอบอื่นนั้นดีกว่า
โจเซฟวู้ด

1
ขอบคุณมากสำหรับการสนับสนุนของคุณ! งานของคุณยอดเยี่ยมมาก!
ThomasIsCoding

5

หลังจากการเรียงลำดับคุณสามารถใช้และduplicatedall

s <- lapply(lst, function(x) lapply(x, sort)) #Sort vectors
s <- lapply(s, function(x) x[order(vapply(x, "[", 1, 1))]) #Sort lists
all(duplicated(s)[-1]) #Test if there are all identical
#length(unique(s)) == 1 #Alternative way to test if all are identical

ทางเลือก: จัดเรียงในหนึ่งวง

s <- lapply(lst, function(x) {
  tt <- lapply(x, sort)
  tt[order(vapply(tt, "[", 1, 1))]
})
all(duplicated(s)[-1])

ทางเลือก: จัดเรียงระหว่างลูปและอนุญาตให้ออกก่อน

s <- lapply(lst[[1]], sort)
s <- s[order(vapply(s, "[", 1, 1))]
tt  <- TRUE
for(i in seq(lst)[-1]) {
  x <- lapply(lst[[i]], sort)
  x <- x[order(vapply(x, "[", 1, 1))]
  if(!identical(s, x)) {
    tt  <- FALSE
    break;
  }
}
tt

หรือใช้ setequal

s <- lapply(lst[[1]], sort)
tt  <- TRUE
for(i in seq(lst)[-1]) {
  x <- lapply(lst[[i]], sort)
  if(!setequal(s, x)) {
    tt  <- FALSE
    break;
  }
}
tt

หรือปรับปรุงความคิดเล็กน้อยจาก@ chinsoon12เพื่อแลกเปลี่ยนรายการด้วยเวกเตอร์!

s <- lst[[1]][order(vapply(lst[[1]], min, 1))]
s <- rep(seq_along(s), lengths(s))[order(unlist(s))]
tt <- TRUE
for(i in seq(lst)[-1]) {
  x <- lst[[i]][order(vapply(lst[[i]], min, 1))]
  x <- rep(seq_along(x), lengths(x))[order(unlist(x))]
  if(!identical(s, x)) {tt <- FALSE; break;}
}
tt

หรือหลีกเลี่ยงการที่สอง order

s <- lst[[1]][order(vapply(lst[[1]], min, 1))]
s <- rep(seq_along(s), lengths(s))[order(unlist(s))]
y <- s
tt <- TRUE
for(i in seq(lst)[-1]) {
  x <- lst[[i]][order(vapply(lst[[i]], min, 1))]
  y <- y[0]
  y[unlist(x)] <- rep(seq_along(x), lengths(x))
  if(!identical(s, y)) {tt <- FALSE; break;}
}
tt

หรือแลกเปลี่ยนorderกับmatch(หรือfmatch)

x <- lst[[1]]
s <- "[<-"(integer(),unlist(x),rep(seq_along(x), lengths(x)))
s <- match(s, unique(s))
tt <- TRUE
for(i in seq(lst)[-1]) {
  x <- lst[[i]]
  y <- "[<-"(integer(),unlist(x),rep(seq_along(x), lengths(x)))
  y <- match(y, unique(y))
  if(!identical(s, y)) {tt <- FALSE; break;}
}
tt

หรือไม่มีทางออกก่อน

s <- lapply(lst, function(x) {
  y <- "[<-"(integer(),unlist(x),rep(seq_along(x), lengths(x)))
  match(y, unique(y))
})
all(duplicated(s)[-1])

หรือเขียนใน C ++

sourceCpp(code = "#include <Rcpp.h>
#include <vector>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::export]]
bool f_GKi_6_Rcpp(const List &x) {
  const List &x0 = x[0];
  const unsigned int n = x0.length();
  unsigned int nn = 0;
  for (List const &i : x0) {nn += i.length();}
  std::vector<int> s(nn);
  for (unsigned int i=0; i<n; ++i) {
    const IntegerVector &v = x0[i];
    for (int const &j : v) {
      if(j > nn) return false;
      s[j-1] = i;
    }
  }
  {
    std::vector<int> lup(n, -1);
    int j = 0;
    for(int &i : s) {
      if(lup[i] < 0) {lup[i] = j++;}
      i = lup[i];
    }
  }
  for (List const &i : x) {
    if(i.length() != n) return false;
    std::vector<int> sx(nn);
    for(unsigned int j=0; j<n; ++j) {
      const IntegerVector &v = i[j];
      for (int const &k : v) {
        if(k > nn) return false;
        sx[k-1] = j;
      }
    }
    {
      std::vector<int> lup(n, -1);
      int j = 0;
      for(int &i : sx) {
        int &lupp = lup[i];
        if(lupp == -1) {lupp = j; i = j++;
        } else {i = lupp;}
      }
    }
    if(s!=sx) return false;
  }
  return true;
}
")

ขอบคุณ @Gregor สำหรับคำแนะนำในการปรับปรุงคำตอบ!


ฉันไม่คิดว่ามันจะใช้งานได้เมื่อมีพาร์ติชันที่มีขนาดเท่ากัน แต่ควรเร็วกว่าของฉันเมื่อมีพาร์ติชันที่มีขนาดไม่เท่ากัน ตัวอย่างเช่นlst <- list(list(1,c(2,3,4),c(5,6),7), list(c(2,3,4),1,7,c(5,6)), list(1,c(2,3,4),7,c(6,5)), list(7,1,c(3,2,4),c(5,6)))จะได้รับการตัดสินเป็นFALSE
ThomasIsCoding เมื่อ

1
@ Gregor ขอบคุณสำหรับเคล็ดลับในการจัดเรียงmin!
GKi

ดูดี! ฉันจะรออีกสักครู่เพื่อดูว่ามีวิธีแก้ปัญหาที่เร็วกว่านี้อีกหรือไม่
ThomasIsCoding

ชุดข้อมูลของคุณเป็นมิติข้อมูลจริงให้คุณหาวิธีแก้ไขปัญหาที่เร็วขึ้นได้อย่างไร
chinsoon12

ฉันเพิ่มมาตรฐานประสิทธิภาพเพื่อดูประสิทธิภาพ (ดูโพสต์ที่แก้ไขใหม่ของฉัน) ทางออกของคุณเร็วกว่าของฉันโดยเฉพาะสองขั้นตอน ฉันต้องการรอจนกว่าโซลูชันใด ๆ ที่มีการปรับปรุงมากขึ้นมิฉะนั้นคุณจะได้รับการยอมรับว่าดีที่สุด ขอขอบคุณอีกครั้ง!
ThomasIsCoding

4

ประสิทธิภาพ:

library(microbenchmark)

microbenchmark(check = 'equal', times=10
  , f_ThomsIsCoding(lst1)
  , f_chinsoon12(lst1)
  , f_GKi_6a(lst1)
  , f_GKi_6b(lst1)
  , f_GKi_6_Rcpp(lst1)
  , f_Rcpp_Hash(lst1))
#Unit: microseconds
#                  expr        min         lq        mean     median         uq        max neval
# f_ThomsIsCoding(lst1) 161187.790 162453.520 167107.5739 167899.471 169441.028 174746.156    10
#    f_chinsoon12(lst1)  64380.792  64938.528  66983.9449  67357.924  68487.438  69201.032    10
#        f_GKi_6a(lst1)   8833.595   9201.744  10377.5844   9407.864  12145.926  14662.022    10
#        f_GKi_6b(lst1)   8815.592   8913.950   9877.4948   9112.924  10941.261  12553.845    10
#    f_GKi_6_Rcpp(lst1)    394.754    426.489    539.1494    439.644    451.375   1327.885    10
#     f_Rcpp_Hash(lst1)    327.665    374.409    499.4080    398.101    495.034   1198.674    10

microbenchmark(check = 'equal', times=10
  , f_ThomsIsCoding(lst2)
  , f_chinsoon12(lst2)
  , f_GKi_6a(lst2)
  , f_GKi_6b(lst2)
  , f_GKi_6_Rcpp(lst2)
  , f_Rcpp_Hash(lst2))
#Unit: microseconds
#                  expr       min        lq        mean      median         uq        max neval
# f_ThomsIsCoding(lst2) 93808.603 99663.651 103358.2039 104676.1600 107124.879 107485.696    10
#    f_chinsoon12(lst2)   131.320   147.192    192.5354    188.1935    205.053    337.062    10
#        f_GKi_6a(lst2)  8630.970  9554.279  10681.9510   9753.2670  11970.377  13489.243    10
#        f_GKi_6b(lst2)    39.736    47.916     61.3929     52.7755     63.026    110.808    10
#    f_GKi_6_Rcpp(lst2)    43.017    51.022     72.8736     76.3465     86.527    116.060    10
#     f_Rcpp_Hash(lst2)     3.667     4.237     20.5887     16.3000     18.031     96.728    10

microbenchmark(check = 'equal', times=10
  , f_ThomsIsCoding(lst3)
  , f_chinsoon12(lst3)
  , f_GKi_6a(lst3)
  , f_GKi_6b(lst3)
  , f_GKi_6_Rcpp(lst3)
  , f_Rcpp_Hash(lst3))
#Unit: microseconds
#                  expr        min         lq        mean      median         uq        max neval
# f_ThomsIsCoding(lst3) 157660.501 166914.782 167067.2512 167204.9065 168055.941 177153.694    10
#    f_chinsoon12(lst3)    139.157    181.019    183.9257    188.0950    198.249    211.860    10
#        f_GKi_6a(lst3)   9484.496   9617.471  10709.3950  10056.1865  11812.037  12830.560    10
#        f_GKi_6b(lst3)     33.583     36.338     47.1577     42.6540     63.469     66.640    10
#    f_GKi_6_Rcpp(lst3)     60.010     60.455     89.4963     94.7220    104.271    121.431    10
#     f_Rcpp_Hash(lst3)      4.404      5.518      9.9811      6.5115     17.396     20.090    10

microbenchmark(check = 'equal', times=10
  , f_ThomsIsCoding(lst4)
  , f_chinsoon12(lst4)
  , f_GKi_6a(lst4)
  , f_GKi_6b(lst4)
  , f_GKi_6_Rcpp(lst4)
  , f_Rcpp_Hash(lst4))
#Unit: milliseconds
#                  expr         min          lq       mean      median          uq        max neval
# f_ThomsIsCoding(lst4) 1874.129146 1937.643431 2012.99077 2002.460746 2134.072981 2187.46886    10
#    f_chinsoon12(lst4)   69.949917   74.393779   80.25362   76.595763   87.116571  100.57917    10
#        f_GKi_6a(lst4)   23.259178   23.328548   27.62690   28.856612   30.675259   32.57509    10
#        f_GKi_6b(lst4)   22.200969   22.326122   24.20769   23.023687   23.619360   31.74266    10
#    f_GKi_6_Rcpp(lst4)    8.062451    8.228526   10.30559    8.363314   13.425531   13.80677    10
#     f_Rcpp_Hash(lst4)    6.551370    6.586025    7.22958    6.724232    6.809745   11.97631    10

ห้องสมุด:

system.time(install.packages("Rcpp"))
#       User      System verstrichen 
#     27.576       1.147      29.396 

system.time(library(Rcpp))
#       User      System verstrichen 
#      0.070       0.000       0.071 

ฟังก์ชั่น:

system.time({f_ThomsIsCoding <- function(lst) {
  s <- Map(function(v) Map(sort,v),lst)
  length(setdiff(Reduce(union,s),Reduce(intersect,s)))==0
}})
#       User      System verstrichen 
#          0           0           0 

#like GKi's solution to stop early when diff is detected
system.time({f_chinsoon12  <- function(lst) {
    x <- lst[[1L]]
    y <- x[order(lengths(x), sapply(x, min))]
    a <- rep(seq_along(y), lengths(y))[order(unlist(y))]
    for(x in lst[-1L]) {
        y <- x[order(lengths(x), sapply(x, min))]
        a2 <- rep(seq_along(y), lengths(y))[order(unlist(y))]
        if(!identical(a, a2)) {
            return(FALSE)
        }
    }
    TRUE
}})
#       User      System verstrichen 
#          0           0           0 

system.time({f_GKi_6a <- function(lst) {
  all(duplicated(lapply(lst, function(x) {
    y <- "[<-"(integer(),unlist(x),rep(seq_along(x), lengths(x)))
    match(y, unique(y))
  }))[-1])
}})
#      User      System verstrichen 
#          0           0           0 

system.time({f_GKi_6b <- function(lst) {
  x <- lst[[1]]
  s <- "[<-"(integer(),unlist(x),rep(seq_along(x), lengths(x)))
  s <- match(s, unique(s))
  for(i in seq(lst)[-1]) {
    x <- lst[[i]]
    y <- "[<-"(integer(),unlist(x),rep(seq_along(x), lengths(x)))
    y <- match(y, unique(y))
    if(!identical(s, y)) return(FALSE)
  }
  TRUE
}})
#       User      System verstrichen 
#          0           0           0 

system.time({sourceCpp(code = "#include <Rcpp.h>
#include <vector>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::export]]
bool f_GKi_6_Rcpp(const List &x) {
  const List &x0 = x[0];
  const unsigned int n = x0.length();
  unsigned int nn = 0;
  for (List const &i : x0) {nn += i.length();}
  std::vector<int> s(nn);
  for (unsigned int i=0; i<n; ++i) {
    const IntegerVector &v = x0[i];
    for (int const &j : v) {
      if(j > nn) return false;
      s[j-1] = i;
    }
  }
  {
    std::vector<int> lup(n, -1);
    int j = 0;
    for(int &i : s) {
      if(lup[i] < 0) {lup[i] = j++;}
      i = lup[i];
    }
  }
  for (List const &i : x) {
    if(i.length() != n) return false;
    std::vector<int> sx(nn);
    for(unsigned int j=0; j<n; ++j) {
      const IntegerVector &v = i[j];
      for (int const &k : v) {
        if(k > nn) return false;
        sx[k-1] = j;
      }
    }
    {
      std::vector<int> lup(n, -1);
      int j = 0;
      for(int &i : sx) {
        int &lupp = lup[i];
        if(lupp == -1) {lupp = j; i = j++;
        } else {i = lupp;}
      }
    }
    if(s!=sx) return false;
  }
  return true;
}
")})
#       User      System verstrichen 
#      3.265       0.217       3.481 

system.time({sourceCpp(code = "#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::plugins(cpp11)]]

void getNPrimes(std::vector<double> &logPrimes) {
    const int n = logPrimes.size();
    const int limit = static_cast<int>(2.0 * static_cast<double>(n) * std::log(n));
    std::vector<bool> sieve(limit + 1, true);
    int lastP = 3;
    const int fsqr = std::sqrt(static_cast<double>(limit));

    while (lastP <= fsqr) {
        for (int j = lastP * lastP; j <= limit; j += 2 * lastP)
            sieve[j] = false;
        int ind = 2;
        for (int k = lastP + 2; !sieve[k]; k += 2)
            ind += 2;
        lastP += ind;
    }
    logPrimes[0] = std::log(2.0);
    for (int i = 3, j = 1; i <= limit && j < n; i += 2)
        if (sieve[i])
            logPrimes[j++] = std::log(static_cast<double>(i));
}

// [[Rcpp::export]]
bool f_Rcpp_Hash(List x) {
    List tempLst = x[0];
    const int n = tempLst.length();
    int myMax = 0;
    // Find the max so we know how many primes to generate
    for (int i = 0; i < n; ++i) {
        IntegerVector v = tempLst[i];
        const int tempMax = *std::max_element(v.cbegin(), v.cend());
        if (tempMax > myMax)
            myMax = tempMax;
    }
    std::vector<double> logPrimes(myMax + 1, 0.0);
    getNPrimes(logPrimes);
    double sumMax = 0.0;
    for (int i = 0; i < n; ++i) {
        IntegerVector v = tempLst[i];
        double mySum = 0.0;
        for (auto j: v)
            mySum += logPrimes[j];
        if (mySum > sumMax)
            sumMax = mySum;
    }
    const uint64_t multiplier = std::numeric_limits<int>::max() / sumMax;
    std::unordered_set<uint64_t> canon;
    canon.reserve(n);
    for (int i = 0; i < n; ++i) {
        IntegerVector v = tempLst[i];
        double mySum = 0.0;
        for (auto j: v)
            mySum += logPrimes[j];
        canon.insert(static_cast<uint64_t>(multiplier * mySum));
    }
    const auto myEnd = canon.end();
    for (auto it = x.begin() + 1; it != x.end(); ++it) {
        List tempLst = *it;
        if (tempLst.length() != n)
            return false;
        for (int j = 0; j < n; ++j) {
            IntegerVector v = tempLst[j];
            double mySum = 0.0;
            for (auto k: v)
                mySum += logPrimes[k];
            const uint64_t key = static_cast<uint64_t>(multiplier * mySum);
            if (canon.find(key) == myEnd)
                return false;
        }
    }
    return true;
}
")})
#       User      System verstrichen 
#      3.507       0.155       3.662 

ข้อมูล:

lst1 <- list(list(1,c(2,3,4),c(5,6)) #TRUE
           , list(c(2,3,4),1,c(5,6))
           , list(1,c(2,3,4),c(6,5)))
lst2 <- list(list(c(2,3,4),c(1,5,6)) #FALSE
           , list(c(2,3,6),c(1,5,4))
           , list(c(2,3,4),c(1,5,6)))
lst3 <- list(list(1,c(2,3,4),c(5,6)) #FALSE
           , list(c(2,3,4),1,c(5,6))
           , list(1,c(2,3,5),c(6,4)))
set.seed(7)
N  <- 1e3
lst1 <- lst1[sample(seq(lst1), N, TRUE)]
lst2 <- lst2[sample(seq(lst2), N, TRUE)]
lst3 <- lst3[sample(seq(lst3), N, TRUE)]
N <- 1000
M <- 500
l <- unname(split(1:N,findInterval(1:N,sort(sample(1:N,N/10)),left.open = T)))
lst4 <- lapply(lapply(1:M, 
                     function(k) lapply(l, 
                                        function(v) v[sample(seq_along(v),length(v))])), function(x) x[sample(seq_along(x),length(x))])

ขอบคุณมาก! ฉันเพิ่งสังเกตเห็นว่าฉันได้พิมพ์ผิดในรหัสของฉันซึ่งควรจะlength(setdiff(Reduce(union,s),Reduce(intersect,s)))==0 ขอโทษสำหรับความผิดพลาดของฉัน ....
34432

@ThomasIsCoding คำตอบอัพเดทแล้ว แต่ฉันทำมันเป็น Wiki ดังนั้นทุกคนจึงยินดีที่จะอัปเดตและรวมถึงโซลูชั่นใหม่ ๆ
GKi

ขอบคุณสำหรับความพยายามของคุณ! ฉันคิดว่าตอนนี้วิธีการแก้ปัญหาของฉันให้ผลลัพธ์เดียวกับคุณหลังจากการแก้ไข แต่ช้ากว่าของคุณ :)
ThomasIsCoding

! น่ากลัว คุณปรับปรุงประสิทธิภาพอย่างน่าทึ่ง! ฉันยอมรับทางออกของคุณ!
ThomasIsCoding

@ chinsoon12 ขอบคุณมากสำหรับการเตือนฉัน! ตอนนี้ฉันได้เปลี่ยนไปใช้การรับรู้ของเขาอีกครั้ง
ThomasIsCoding

3

หวังว่าครั้งที่ 2 จะโชคดี

f <- function(lst) {
    s <- lapply(lst, function(x) {
        y <- x[order(lengths(x), sapply(x, min))]
        rep(seq_along(y), lengths(y))[order(unlist(y))]
    })
    length(unique(s))==1L
}

กรณีทดสอบ:

# should return `TRUE`
lst1 <- list(list(1,c(2,3,4),c(5,6)),
    list(c(2,3,4),1,c(5,6)),
    list(1,c(2,3,4),c(6,5)))

# should return `TRUE`
lst2 <- list(list(1:2, 3:4), list(3:4, 1:2))

# should return `FALSE`
lst3 <- list(list(1,c(2,3,4),c(5,6)), list(c(2,3,4),1,c(5,6)), list(1,c(2,3,5),c(6,4)))

# should return `FALSE`
lst4 <- list(list(c(2,3,4),c(1,5,6)), list(c(2,3,6),c(1,5,4)), list(c(2,3,4),c(1,5,6)))

lst5 <- list(list(1,c(2,3,4),c(5,6)) #TRUE
    , list(c(2,3,4),1,c(5,6))
    , list(1,c(2,3,4),c(6,5)))
lst6 <- list(list(c(2,3,4),c(1,5,6)) #FALSE
    , list(c(2,3,6),c(1,5,4))
    , list(c(2,3,4),c(1,5,6)))
lst7 <- list(list(1,c(2,3,4),c(5,6)) #FALSE
    , list(c(2,3,4),1,c(5,6))
    , list(1,c(2,3,5),c(6,4)))

การตรวจสอบ:

f(lst1)
#[1] TRUE
f(lst2)
#[1] TRUE
f(lst3)
#[1] FALSE
f(lst4)
#[1] FALSE
f(lst5)
#[1] TRUE
f(lst6)
#[1] FALSE
f(lst7)
#[1] FALSE

รหัสเวลา:

library(microbenchmark)
set.seed(0L)
N <- 1000
M <- 100
l <- unname(split(1:N,findInterval(1:N,sort(sample(1:N,N/10)),left.open = T)))
lst <- lapply(lapply(1:M,
    function(k) lapply(l,
        function(v) v[sample(seq_along(v),length(v))])), function(x) x[sample(seq_along(x),length(x))])

f_ThomsIsCoding <- function(lst) {
    s <- Map(function(v) Map(sort,v),lst)
    length(setdiff(Reduce(union,s),Reduce(intersect,s)))==0
}

f_GKi_1 <- function(lst) {
    all(duplicated(lapply(lst, function(x) lapply(x, sort)[order(unlist(lapply(x, min)))]))[-1])
}

f_GKi_2 <- function(lst) {
    s <- lapply(lst, function(x) lapply(x, sort))
    all(duplicated(lapply(s, function(x) x[order(unlist(lapply(x, "[", 1)))]))[-1])
}


f <- function(lst) {
    s <- lapply(lst, function(x) {
        y <- x[order(lengths(x), sapply(x, min))]
        rep(seq_along(y), lengths(y))[order(unlist(y))]
    })
    length(unique(s))==1L
}

microbenchmark(times=3L,
    f_ThomsIsCoding(lst),
    f_GKi_1(lst),
    f_GKi_2(lst),
    f(lst)
)

การกำหนดเวลา:

Unit: milliseconds
                 expr       min        lq      mean    median        uq      max neval
 f_ThomsIsCoding(lst) 333.77313 334.61662 348.37474 335.46010 355.67555 375.8910     3
         f_GKi_1(lst) 324.12827 324.66580 326.33016 325.20332 327.43111 329.6589     3
         f_GKi_2(lst) 315.73533 316.05770 333.35910 316.38007 342.17099 367.9619     3
               f(lst)  12.42986  14.08256  15.74231  15.73526  17.39853  19.0618     3

ใช่มันใช้งานได้ดีในเวลานี้
ThomasIsCoding
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.