سؤال

هذا هو نص طويل.يرجى تتحمل معي.المغلي, السؤال هو: هل هناك عملي في مكان الرقم الأساسي خوارزمية الفرز?


الأولي

لقد حصلت على عدد كبير من الصغيرة ذات طول ثابت سلاسل فقط استخدام الحروف "ألف" و "ج" و "ز" و "T" (نعم ، لقد خمنت ذلك: الحمض النووي) أريد أن النوع.

في هذه اللحظة يمكنني استخدام std::sort والذي يستخدم introsort في جميع التنفيذ المشتركة المحكمة الخاصة بلبنان.هذا يعمل بشكل جيد جدا.ولكن أنا مقتنع بأن radix نوع يناسب مشكلتي تعيين تماما و يجب أن تعمل كثيرا أفضل في الممارسة العملية.

التفاصيل

لقد اختبرت هذا مع افتراض ساذج جدا والتنفيذ للمشاريع الصغيرة نسبيا المدخلات (على أمر من 10,000) كان هذا صحيحا (حسنا, على الأقل أكثر من أسرع مرتين).ومع ذلك ، runtime يحط ذريعا عندما تكون المشكلة حجم يصبح أكبر (N > 5,000,000).

والسبب واضح:radix النوع يتطلب نسخ البيانات كاملة (أكثر من مرة في السذاجة التنفيذ في الواقع).وهذا يعني أن كنت قد وضعت ~ 4 بنك الخليج الدولي في الذاكرة الرئيسية والتي من الواضح يقتل الأداء.حتى لو لم لا يمكنني استخدام هذه الذاكرة كثيرا منذ مشكلة الأحجام في الواقع حتى تصبح أكبر.

حالات الاستخدام

من الناحية المثالية, هذه الخوارزمية يجب أن تعمل مع أي طول السلسلة بين 2 و 100 الحمض النووي وكذلك DNA5 (الذي يسمح إضافية بدل حرف "N") ، أو حتى مع الحمض النووي الاتحاد الدولي للكيمياء البحتة والتطبيقية غموض رموز (مما أدى إلى 16 متميزة القيم).إلا أنني أدرك أن كل هذه الحالات لا يمكن تغطيتها ، لذلك أنا سعيد مع أي تحسين سرعة الحصول على.رمز يمكن أن تقرر بشكل حيوي التي الخوارزمية إلى إيفاد.

البحث

للأسف ، مقالة ويكيبيديا على الرقم الأساسي نوع لا طائل منه.قسم حول في المكان البديل كاملة القمامة.على NIST-الآباء القسم على الرقم الأساسي نوع بجانب غير موجود.هناك واعدة السبر ورقة تسمى كفاءة التكيف في مكان الرقم الأساسي الفرز الذي يصف خوارزمية "MSL".للأسف هذه الورقة أيضا مخيبة للآمال.

لا سيما أن هناك من الأشياء التالية.

أولا الخوارزمية يحتوي على العديد من الأخطاء و يترك الكثير غير المبررة.ولا سيما أنه لا تفاصيل مكالمة العودية (أنا ببساطة نفترض أنه من زيادات أو يقلل من بعض المؤشر لحساب التحول الحالي و قناع القيم).كما أنه يستخدم وظائف dest_group و dest_address دون إعطاء التعاريف.لا أستطيع أن أرى كيف أن تنفيذ هذه بكفاءة (وهذا هو ، في س(1);على الأقل dest_address ليست تافهة).

وأخيرا وليس آخرا ، فإن خوارزمية يحقق في مكان نيس طريق مبادلة مجموعة المؤشرات مع عناصر داخل مجموعة الإدخال.ومن الواضح أن هذا يعمل فقط على المصفوفات العددية.كنت بحاجة إلى استخدام على السلاسل.بالطبع, أنا فقط يمكن أن المسمار الكتابة قوية و المضي قدما على افتراض أن الذاكرة لن يتسامح مع بلدي تخزين فهرس حيث أنها لا تنتمي.ولكن هذا يعمل فقط طالما أنا يمكن أن يضغط سلاسل بلدي إلى 32 بت من الذاكرة (على افتراض 32 بت الصحيحه).هذا فقط 16 حرفا (دعونا نتجاهل لحظة أن 16 > سجل(5,000,000)).

ورقة أخرى من قبل أحد المؤلفين لا يعطي وصف دقيق في كل شيء ، لكنه يعطي MSL وقت التشغيل شبه الخطية الذي هو خارج الشقة من الخطأ.

باختصار:هل هناك أي أمل في العثور على عمل إشارة التنفيذ أو على الأقل شبة الكود/وصف العمل في مكان الرقم الأساسي النوع الذي يعمل على الحمض النووي السلاسل ؟

هل كانت مفيدة؟

المحلول

حسنا, هنا بسيط تنفيذ MSD radix نوع الحمض النووي.إنه مكتوب في د لأن هذه هي اللغة التي تستخدم في معظم وبالتالي أنا أقل من المحتمل أن تجعل أخطاء سخيفة ، ولكن يمكن بسهولة أن تترجم إلى لغة أخرى.انها في نفس المكان ولكن يتطلب 2 * seq.length يمر من خلال مجموعة.

void radixSort(string[] seqs, size_t base = 0) {
    if(seqs.length == 0)
        return;

    size_t TPos = seqs.length, APos = 0;
    size_t i = 0;
    while(i < TPos) {
        if(seqs[i][base] == 'A') {
             swap(seqs[i], seqs[APos++]);
             i++;
        }
        else if(seqs[i][base] == 'T') {
            swap(seqs[i], seqs[--TPos]);
        } else i++;
    }

    i = APos;
    size_t CPos = APos;
    while(i < TPos) {
        if(seqs[i][base] == 'C') {
            swap(seqs[i], seqs[CPos++]);
        }
        i++;
    }
    if(base < seqs[0].length - 1) {
        radixSort(seqs[0..APos], base + 1);
        radixSort(seqs[APos..CPos], base + 1);
        radixSort(seqs[CPos..TPos], base + 1);
        radixSort(seqs[TPos..seqs.length], base + 1);
   }
}

ومن الواضح أن هذا هو نوع من أنواع محددة من الحمض النووي, بدلا من كونها عامة ، ولكن يجب أن تكون سريعة.

تحرير:

لدي فضول ما إذا كان هذا الكود يعمل حتى اختبرت/تصحيحه أنه في انتظار بلدي المعلوماتية الحيوية بتشغيل التعليمات البرمجية.الإصدار أعلاه الآن هو في الواقع اختبار يعمل.مقابل 10 ملايين متواليات من 5 قواعد كل ذلك عن 3x أسرع من الأمثل introsort.

نصائح أخرى

لم أر في مكان الرقم الأساسي النوع من طبيعة الجذر-نوع أشك أنه هو أسرع بكثير من الخروج من المكان نوعا ما دام مؤقتة مجموعة يناسب في الذاكرة.

السبب:

الفرز يفعل الخطية قراءة في مجموعة الإدخال ، ولكن كل ما يكتب سيكون ما يقرب من العشوائية.من معين ن صعودا هذا يتلخص ملكة جمال مخبأ في الكتابة.ذاكرة التخزين المؤقت هذه الآنسة هو ما يبطئ خوارزمية الخاص بك.إذا كان في مكان أو لا لن يغير هذا التأثير.

أنا أعلم أن هذا لن أجيب على سؤالك مباشرة ، ولكن إذا الفرز هو عنق الزجاجة قد ترغب في إلقاء نظرة على بالقرب من الفرز خوارزميات كما تجهيز خطوة (ويكي-صفحة التطبيقات إلى كومة قد بدأت تحصل).

التي يمكن أن تعطي لطيفة جدا ذاكرة التخزين المؤقت محلة دفعة.نص كتاب خارج المكان radix نوعا ما ثم أداء أفضل.يكتب سوف يكون لا يزال ما يقرب من عشوائية ولكن على الأقل أنها سوف تتجمع حول نفس قطع من الذاكرة مثل زيادة نسبة استرداد ذاكرة التخزين المؤقت.

ليس لدي أي فكرة ما إذا كان يعمل بها في الممارسة العملية على الرغم من.

راجع للشغل:إذا كنت تتعامل مع سلاسل الحمض النووي فقط:يمكنك ضغط شار إلى اثنين بت حزمة البيانات الخاصة بك الكثير جدا.هذا سوف يقلل من متطلبات الذاكرة من قبل عامل أكثر من أربعة ناييف التمثيل.معالجة يصبح أكثر تعقيدا ، ولكن ALU وحدة المعالجة المركزية الخاصة بك لديها الكثير من الوقت لقضاء خلال كل ذاكرة التخزين المؤقت-يخطئ على أي حال.

أنت بالتأكيد يمكن إسقاط متطلبات الذاكرة من خلال ترميز تسلسل في بت.كنت تبحث في التقليب حتى, على طول 2 ، "ACGT" أن 16 دولة ، أو 4 بت.على طول 3 ، 64 دولة ، والتي يمكن ترميز في 6 أجزاء.بحيث يبدو مثل 2 بت لكل حرف في تسلسل أو 32 بت 16 حرفا كما قلت.

إذا كان هناك طريقة للحد من عدد من صالح 'كلمات', مزيد من الضغط قد يكون ممكنا.

حتى تسلسل طول 3, يمكن للمرء أن إنشاء 64 الدلاء ، ربما الحجم uint32 ، أو uint64.تهيئة لهم إلى الصفر.من خلال تكرار بك جدا قائمة كبيرة جدا من 3 شار متواليات ، و ترميز لهم على النحو الوارد أعلاه.استخدام هذه منخفض, و الزيادة التي دلو.
كرر هذا حتى كل من متواليات تمت معالجتها.

المقبل, تجديد القائمة الخاصة بك.

من خلال تكرار 64 دلاء من اجل الاعتماد وجدت في هذا الدلو ، وتوليد أن العديد من حالات تسلسل يمثله هذا الدلو.
عند كل من الدلاء تم يتحرك, لديك مجموعة مرتبة.

سلسلة من 4 ، ويضيف 2 بت ، لذلك لن يكون هناك 256 الدلاء.سلسلة من 5 ، ويضيف 2 بت ، لذلك لن يكون هناك 1024 الدلاء.

في بعض نقطة في عدد من الدلاء نهج حدودك.إذا كنت تقرأ متواليات من ملف بدلا من الاحتفاظ بها في الذاكرة, ذاكرة أكثر سوف تكون متاحة الدلاء.

وأعتقد أن هذا سيكون أسرع من القيام النوع في الموقع كما الدلاء من المرجح أن تناسب داخل مجموعة العمل.

هنا هو الإختراق الذي يظهر تقنية

#include <iostream>
#include <iomanip>

#include <math.h>

using namespace std;

const int width = 3;
const int bucketCount = exp(width * log(4)) + 1;
      int *bucket = NULL;

const char charMap[4] = {'A', 'C', 'G', 'T'};

void setup
(
    void
)
{
    bucket = new int[bucketCount];
    memset(bucket, '\0', bucketCount * sizeof(bucket[0]));
}

void teardown
(
    void
)
{
    delete[] bucket;
}

void show
(
    int encoded
)
{
    int z;
    int y;
    int j;
    for (z = width - 1; z >= 0; z--)
    {
        int n = 1;
        for (y = 0; y < z; y++)
            n *= 4;

        j = encoded % n;
        encoded -= j;
        encoded /= n;
        cout << charMap[encoded];
        encoded = j;
    }

    cout << endl;
}

int main(void)
{
    // Sort this sequence
    const char *testSequence = "CAGCCCAAAGGGTTTAGACTTGGTGCGCAGCAGTTAAGATTGTTT";

    size_t testSequenceLength = strlen(testSequence);

    setup();


    // load the sequences into the buckets
    size_t z;
    for (z = 0; z < testSequenceLength; z += width)
    {
        int encoding = 0;

        size_t y;
        for (y = 0; y < width; y++)
        {
            encoding *= 4;

            switch (*(testSequence + z + y))
            {
                case 'A' : encoding += 0; break;
                case 'C' : encoding += 1; break;
                case 'G' : encoding += 2; break;
                case 'T' : encoding += 3; break;
                default  : abort();
            };
        }

        bucket[encoding]++;
    }

    /* show the sorted sequences */ 
    for (z = 0; z < bucketCount; z++)
    {
        while (bucket[z] > 0)
        {
            show(z);
            bucket[z]--;
        }
    }

    teardown();

    return 0;
}

إذا كانت البيانات الخاصة بك هو كبير جدا ، ثم أعتقد أن يستند إلى قرص العازلة النهج سيكون أفضل:

sort(List<string> elements, int prefix)
    if (elements.Count < THRESHOLD)
         return InMemoryRadixSort(elements, prefix)
    else
         return DiskBackedRadixSort(elements, prefix)

DiskBackedRadixSort(elements, prefix)
    DiskBackedBuffer<string>[] buckets
    foreach (element in elements)
        buckets[element.MSB(prefix)].Add(element);

    List<string> ret
    foreach (bucket in buckets)
        ret.Add(sort(bucket, prefix + 1))

    return ret

وأود أيضا أن تجربة التجمع إلى أكبر عدد من الدلاء ، على سبيل المثال ، إذا كانت السلسلة:

GATTACA

أول MSB دعوة سيعود دلو على الجات (256 مجموع دلاء), وبهذه الطريقة يمكنك جعل عدد أقل من فروع القرص على أساس العازلة.هذا قد أو قد لا تعمل على تحسين الأداء ، لذلك التجربة معها.

انا ذاهب الى الخروج على أطرافهم و أقترح عليك التبديل إلى كومة/heapsort التنفيذ.هذا الاقتراح يأتي مع بعض الافتراضات:

  1. يمكنك التحكم في قراءة البيانات
  2. يمكنك أن تفعل شيء مفيد مع فرز البيانات في أقرب وقت كما كنت 'ابدأ' الحصول عليها تم فرزها.

جمال كومة/هيب-النوع هو أنه يمكنك بناء كومة أثناء قراءة البيانات ، يمكنك البدء في الحصول على نتائج لحظة كنت قد بنيت الكومة.

دعونا خطوة الى الوراء.إذا كنت محظوظا يمكنك قراءة البيانات بشكل غير متزامن (وهذا هو ، يمكنك إضافة نوع من قراءة طلب أن يتم إعلامك عند بعض البيانات جاهزة), ثم يمكنك بناء قطعة من كومة بينما كنت في انتظار القادم قطعة من البيانات أن تأتي حتى من القرص.في كثير من الأحيان ، هذا النهج يمكن دفن أكثر من تكلفة نصف الفرز وراء الوقت المستغرق في الحصول على البيانات.

مرة واحدة كنت قد قراءة البيانات ، العنصر الأول هو متاح بالفعل.اعتمادا على حيث يمكنك إرسال البيانات ، وهذا يمكن أن تكون كبيرة.إذا كنت تقوم بإرسال آخر غير متزامن القارئ أو بعض موازية 'الحدث' نموذج أو واجهة المستخدم, يمكنك إرسال قطع و قطع كما تذهب.

أن قال - إذا كان لديك أي سيطرة على كيفية قراءة البيانات ، يتم قراءتها بشكل متزامن ، ، يجب استخدام فرز البيانات حتى هو مكتوب تماما من الخروج تجاهل كل هذا.:(

انظر مقالات ويكيبيديا:

الحكمة قد ترغب في النظر في سلسلة مقارنة خوارزميات الفرز.

حاليا الرياح لمس كل عنصر من كل سلسلة ، ولكن يمكنك أن تفعل أفضل!

على وجه الخصوص ، انفجار نوع هو مناسبا جدا لهذه الحالة.على سبيل المكافأة ، منذ burstsort يستند يحاول انه يعمل على السخرية جيدا الصغيرة الأبجدية الأحجام المستخدمة في DNA/RNA, منذ كنت لا تحتاج إلى بناء أي نوع من الثلاثي البحث عقدة التجزئة أو غيرها trie عقدة نظام ضغط في trie التنفيذ.المحاولات قد تكون مفيدة بالنسبة لاحقة-مجموعة-مثل الهدف النهائي أيضا.

لائق الغرض العام تنفيذ burstsort تتوفر على مصدر المضي في http://sourceforge.net/projects/burstsort/ - ولكن ليس في نفس المكان.

لأغراض المقارنة ، ج-burstsort تنفيذ مغطاة في http://www.cs.mu.oz.au/~rsinha/أوراق/SinhaRingZobel-2006.pdf المعايير 4-5x أسرع من فرز سريع والجذر أنواع لبعض نموذجية أعباء العمل.

أنت تريد أن تأخذ نظرة على الجينوم على نطاق واسع تسلسل المعالجة من قبل الدكاترة.كاساهارا و موريشيتا.

سلاسل تتألف من أربعة النوكليوتيدات الحروف A, C, G, T يمكن خصيصا المشفرة في الاعداد الصحيحه كثيرا معالجة أسرع.Radix النوع هو من بين العديد من الخوارزميات التي نوقشت في الكتاب ؛ يجب أن تكون قادرة على التكيف مع قبول الإجابة على هذا السؤال ومعرفة كبيرة لتحسين الأداء.

"Radix يصنف مع أي مساحة إضافية"هو ورقة معالجة المشكلة.

قد حاول استخدام trie.فرز البيانات ببساطة بالتكرار عبر البيانات وإدراج ذلك ؛ هيكل هو بطبيعة الحال فرز و يمكنك التفكير في الأمر على النحو مماثلة ب-شجرة (باستثناء بدلا من إجراء مقارنات ، دائما استخدام مؤشر indirections).

سلوك التخزين المؤقت سيكون لصالح كل من العقد الداخلية ، لذلك ربما لن تحسين على ذلك ؛ ولكن يمكنك كمان مع المتفرعة عامل من trie وكذلك (التأكد من أن كل عقدة يلائم واحدة ذاكرة التخزين المؤقت الخط تخصيص trie العقد مماثلة إلى كومة ، متجاورة مجموعة تمثل المستوى من أجل اجتياز).منذ يحاول أيضا الرقمية الهياكل (O(k) إدراج/على/حذف عناصر من طول k) ، يجب أن يكون الأداء التنافسي إلى الرقم الأساسي النوع.

أود أن burstsort معبأة بت تمثيل السلاسل.Burstsort ادعت أن لديها أفضل بكثير محلة من الجذر أنواع, حفظ مساحة إضافية الاستخدام مع انفجار يحاول في مكان الكلاسيكية يحاول.الورقة الأصلية قد القياسات.

Radix-النوع لا ذاكرة التخزين المؤقت واعية و ليست أسرع نوع خوارزمية مجموعات كبيرة.يمكنك أن تبحث في:

يمكنك أيضا استخدام ضغط و تشفير كل حرف من الحمض النووي الخاص بك في 2 بت قبل تخزينها في مجموعة النوع.

dsimcha هو MSB radix النوع تبدو لطيفة ولكن نيلس يحصل أقرب إلى قلب المشكلة مع ملاحظة أن ذاكرة التخزين المؤقت محلة ما قتل لك في مشكلة كبيرة الأحجام.

أقترح نهج بسيط جدا:

  1. تجريبيا تقدير حجم أكبر m التي radix النوع هو كفاءة.
  2. قراءة كتل من m العناصر في وقت واحد ، radix فرزها و الكتابة بها (إلى المخزن المؤقت الذاكرة إذا كان لديك ما يكفي من الذاكرة ، ولكن على خلاف ذلك الملف) حتى العادم المدخلات الخاصة بك.
  3. Mergesort مما أدى فرز كتل.

Mergesort هو الأكثر ذاكرة التخزين المؤقت الصديقة خوارزمية الفرز أنا على علم:"قراءة البند التالي من أي مجموعة A أو B ، ثم كتابة عنصر إلى المخزن المؤقت للإخراج." فإنه يعمل بكفاءة على محركات أقراص الشريط.أنها لا تتطلب 2n مساحة نوعا ما n البنود, ولكن أراهن أن الكثير-تحسين ذاكرة التخزين المؤقت محلة سترى من شأنها جعل ذلك غير مهم .. و إذا كنت تستخدم غير في مكان الرقم الأساسي النوع ، تحتاج مساحة إضافية على أي حال.

يرجى ملاحظة أخيرا أن mergesort يمكن تنفيذها دون العودية, و في الواقع بهذه الطريقة يوضح حقيقة الخطية ذاكرة الوصول نمط.

يبدو أنك قد حللت المشكلة, لكن للعلم ، يبدو أن نسخة واحدة من عملي في مكان الرقم الأساسي النوع هو "العلم الأمريكي النوع".إنه هو موضح هنا: الهندسة Radix نوع.الفكرة العامة هي أن تفعل 2 يمر على كل حرف أول إحصاء عدد كل لديك, حيث يمكنك تقسيم مجموعة الإدخال في صناديق.ثم تذهب من خلال مرة أخرى ، مبادلة كل عنصر في بن الصحيح.الآن متكرر نوع كل بن على الحرف التالي الموقف.

أولا التفكير في الترميز المشكلة.التخلص من السلاسل ، استبدالها من قبل تمثيل ثنائي.استخدام البايت الأول للإشارة إلى طول+ترميز.بدلا من ذلك استخدام ثابت طول التمثيل في أربعة بايت.ثم radix النوع يصبح أسهل بكثير.عن الرقم الأساسي نوعا ما, الشيء الأكثر أهمية هو أن لا يكون التعامل مع استثناء في بقعة ساخنة من الحلقة الداخلية.

حسنا, أعتقد أكثر قليلا عن 4-نارى المشكلة.تريد حل مثل جودي شجرة من أجل هذا.الحل التالي يمكن التعامل مع متغير طول سلاسل ؛ على طول ثابت فقط إزالة طول بت ، هذا الواقع يجعل من الأسهل.

تخصيص كتل من 16 المؤشرات.البت الأقل أهمية من المؤشرات يمكن إعادة استخدامها ، كما كتل الخاصة بك وسوف يكون دائما الانحياز.قد ترغب في تخزين خاصة مخصص لذلك (كسر التخزين الكبيرة إلى كتل أصغر).هناك عدد من أنواع مختلفة من كتل:

  • ترميز مع 7 طول أجزاء من سلاسل أحرف ذات طول متغير.كما أنها تملأ ، استبدالها من قبل:
  • موقف بترميز المقبلين الشخصيات لديك 16 المؤشرات التالية كتل تنتهي:
  • نقطية ترميز الأحرف الثلاثة الأخيرة من سلسلة.

لكل نوع من كتلة ، تحتاج إلى تخزين معلومات مختلفة في LSBs.كما كنت قد السلاسل ذات الطول المتغير تحتاج إلى تخزين نهاية السلسلة أيضا ، و آخر نوع من كتلة يمكن استخدامها فقط لمدة أطول السلاسل.7 طول بت بعبارة أقل كما يمكنك الحصول على أعمق في الهيكل.

هذا يوفر لك معقول سريع جدا و كفاءة الذاكرة تخزين فرز السلاسل.وسوف تتصرف نوعا ما مثل trie.للحصول على هذا العمل ، للتأكد من بناء ما يكفي من وحدة الاختبارات.تريد تغطية كل كتلة التحولات.كنت تريد أن تبدأ مع فقط النوع الثاني من كتلة.

لمزيد من الأداء ، قد ترغب في إضافة أنواع مختلفة كتلة و حجم أكبر من كتلة.إذا كانت كتل هي دائما نفس الحجم و كبيرة بما فيه الكفاية, يمكنك استخدام عدد أقل من البتات المؤشرات.مع كتلة حجم 16 المؤشرات لديك بالفعل بايت مجانا في 32 بت مساحة العنوان.نلقي نظرة على جودي شجرة وثائق مثيرة للاهتمام أنواع كتلة.أساسا ، يمكنك إضافة التعليمات البرمجية والهندسية الوقت للحصول على مساحة (وقت التشغيل) المفاضلة

ربما كنت تريد أن تبدأ مع 256 واسعة المباشر الرقم الأساسي لأول أربعة أحرف.التي توفر الكريم الزمان والمكان المقايضة.في هذا التطبيق ، يمكنك الحصول على أقل بكثير من الذاكرة من فوق مع بسيطة trie;وهو ما يقرب من ثلاث مرات أصغر (لم قياسها).O(n) لا توجد مشكلة إذا كان ثابت منخفض بما فيه الكفاية ، كما لاحظت عند مقارنة مع O(n log n) فرز سريع.

هل أنت مهتم في التعامل المزدوج ؟ مع تسلسل قصيرة, ستكون هناك.التكيف مع الكتل للتعامل مع التهم صعبة ، ولكن يمكن أن تكون جدا فعالة من حيث المساحة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top