2012-03-28 13 views
5

सी # का उपयोग करके, अज्ञात आयामों की एक बहुआयामी सरणी के माध्यम से कैसे पुनरावृत्ति करें?मनमानी आयाम की एक सरणी के माध्यम से Iterate

उदाहरण के लिए सरणी के प्रत्येक तत्व को निर्दिष्ट मान पर सेट करने पर विचार करें, सरणी की सभी प्रविष्टियों को पुनरावृत्त करने की आवश्यकता है। विधि को निम्नलिखित सभी मामलों को संभालना चाहिए और सरणी के आयाम के बावजूद, सभी प्रविष्टियों को मान 4 के साथ भरना चाहिए।

ClearArray(new int[3], 4); 
ClearArray(new int[3,3], 4); 
ClearArray(new int[3, 3, 3, 3], 4); 

तरीकों हस्ताक्षर स्पष्ट रूप से लगता है कि

static void ClearArray(Array a, int val) { ... } 

मैं जानता हूँ कि एक आयाम के माध्यम से पुनरावृति करने के लिए कैसे कुछ:

for (int i=0; i<a.GetLength(dimension); i++) 
{ 
    ... 
} 

नोट: यह सवाल 2 डी सरणियों के बारे में नहीं है , 3 डी सरणी, न ही 4 डी सरणी। Array ऑब्जेक्ट पर Rank संपत्ति के किसी भी आयाम को संभालना चाहिए।

उत्तर

5

सबसे तेजी से समाधान Buffer.BlockCopy है:

static void ClearArray(Array array, int val) 
{ 
    var helper = Enumerable.Repeat(val, Math.Min(array.Length, 1024)).ToArray(); 
    var itemSize = 4; 


    Buffer.BlockCopy(helper, 0, array, 0, helper.Length * itemSize); 
    for (var len = helper.Length; len < array.Length; len *= 2) 
    { 
    Buffer.BlockCopy(array, 0, array, len * itemSize, Math.Min(len, array.Length - len) * itemSize); 
    } 
} 

static int Count(Array array, Func<int, bool> predicate) 
{ 
    var helper = new int[Math.Min(array.Length, 4096)]; 
    var itemSize = 4; 

    var count = 0; 
    for (var offset = 0; offset < array.Length; offset += helper.Length) 
    { 
    var len = Math.Min(helper.Length, array.Length - offset); 
    Buffer.BlockCopy(array, offset * itemSize, helper, 0, len * itemSize); 
    for (var i = 0; i < len; ++i) 
     if (predicate(helper[i])) 
     count++; 
    } 
    return count; 
} 

आँकड़ा:

time: 00:00:00.0449501, method: Buffer.BlockCopy 
time: 00:00:01.4371424, method: Lexicographical order 
time: 00:00:01.3588629, method: Recursed 
time: 00:00:06.2005057, method: Cartesian product with index array reusing 
time: 00:00:08.2433531, method: Cartesian product w/o index array reusing 

आँकड़ा (समारोह गणना):

time: 00:00:00.0812866, method: Buffer.BlockCopy 
time: 00:00:02.7617093, method: Lexicographical order 

कोड:

Array array = Array.CreateInstance(typeof(int), new[] { 100, 200, 400 }, new[] { -10, -20, 167 }); 
    foreach (var info in new [] 
    { 
     new {Name = "Buffer.BlockCopy", Method = (Action<Array, int>)ClearArray_BufferCopy}, 
     new {Name = "Lexicographical order", Method = (Action<Array, int>)ClearArray_LexicographicalOrder}, 
     new {Name = "Recursed", Method = (Action<Array, int>)ClearArray_Recursed}, 
     new {Name = "Cartesian product with index array reusing", Method = (Action<Array, int>)ClearArray_Cartesian_ReuseArray}, 
     new {Name = "Cartesian product w/o index array reusing", Method = (Action<Array, int>)ClearArray_Cartesian}, 
    } 
    ) 
    { 
    var stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    var count = 10; 
    for (var i = 0; i < count; ++i) 
     info.Method(array, i); 
    stopwatch.Stop(); 
    Console.WriteLine("time: {0}, method: {1}", TimeSpan.FromTicks(stopwatch.Elapsed.Ticks/count), info.Name); 
    } 
+0

+1 नीचे मापने देखें! यद्यपि एक सरणी भरने के लिए केवल काम करता है, इसके माध्यम से नहीं चल रहा है। उदाहरण के लिए गैर-शून्य प्रविष्टियों की संख्या गिनें या इस तस्वीर में फिट नहीं होंगे। – vidstige

+0

@ vidstige काउंटर विधि Buffer.BlockCopy के साथ किसी भी तरह से सबसे ऊपर है (ऊपर उदाहरण देखें)। एरिक को बहु-मंद सरणी पसंद नहीं है और इसलिए उन्होंने धीरे-धीरे .NET में काम किया :) –

0

आयामों की संख्या निर्धारित करने के लिए Array.Rank का उपयोग करें, फिर प्रत्येक दिए गए रैंक के लिए सीमा को समझने के लिए Array.GetLowerBound (int आयाम) और Array.GetUpperBound (int आयाम) का उपयोग करें।

यह निर्दिष्ट नहीं है कि आपके इटरेटर को कैसे काम करना चाहिए (उदा। पुनरावृत्ति के क्रम में कोई अर्थपूर्ण है)। हालांकि, ClearArray() को लागू करने के लिए पुनरावृत्ति के आदेश से कोई फर्क नहीं पड़ता।

अपनी ClearArray विधि के लिए निर्दिष्ट मान सेट करने के लिए एक SetValue (ऑब्जेक्ट वैल्यू, पैराम्स int [] इंडेक्स) का उपयोग करें।

+0

हाँ। मैंने भी यह पाया। हम 0-ऐरे का उपयोग भी कर सकते हैं। गेटलेथेंथ, है ना? लेकिन यह सब टॉगर कैसे सीना है? पुनरावर्ती? क्या इसे देख सकते हैं। – vidstige

+0

पुनरावृत्ति का आदेश कोई फर्क नहीं पड़ता, कोई भी आदेश काम करेगा। लेकिन प्रत्येक तत्व को केवल एक बार उपयोग किया जाना है। और जादू सूचकांक [] तर्क SetValue को पारित किया? हम इसकी गणना कैसे करते हैं? यहां सरणी के वास्तविक पुनरावृत्ति निहित है। – vidstige

7

Lexicographical order का उपयोग करें:
अनुक्रमणिका "अंक" का अनुक्रम है। प्रत्येक यात्रा पिछले "अंक" सीमा में, जबकि यह संख्या बढ़ पर, फिर अगले "अंक" वृद्धि, आदि

Func<Array, int[]> firstIndex = 
    array => Enumerable.Range(0, array.Rank) 
     .Select(_i => array.GetLowerBound(_i)) 
     .ToArray(); 

Func<Array, int[], int[]> nextIndex = (array, index) => 
    { 
    for (int i = index.Length-1; i >= 0; --i) 
    { 
     index[i]++; 
     if (index[i] <= array.GetUpperBound(i)) 
     return index; 
     index[i] = array.GetLowerBound(i); 
    } 
    return null; 
    }; 

for (var index = firstIndex(array); index != null; index = nextIndex(array, index)) 
{ 
    var v = array.GetValue(index); 
    ... 
    array.SetValue(newValue, index); 
} 
+0

चालाक! लेकिन शब्दावली क्रम के माध्यम से कैसे पुनरावृत्त करने के लिए? अगर मैं [0,0, ..., 0] से शुरू करता हूं तो अगले सूचकांक क्या हैं? और मुझे कैसे पता चलेगा कि सभी सूचकांक कब पुनरावृत्त होते हैं? – vidstige

+0

धन्यवाद! एक जादू की तरह काम करता है। वहां कुछ quirk यह प्रत्येक आयाम में अंतिम सूचकांक छोड़ देता है। – vidstige

+1

"अंतिम अनुक्रमणिका छोड़ दी गई" बग सही –

2

एक साधारण से 2-चरणीय समाधान, कोई अनुकूलन का प्रयास किया:

public static void ClearArray(Array a, int val) 
    { 
     int[] indices = new int[a.Rank]; 
     ClearArray(a, 0, indices, val); 
    } 

    private static void ClearArray(Array a, int r, int[] indices, int v) 
    { 
     for (int i = 0; i < a.GetLength(r); i++) 
     { 
      indices[r] = i; 

      if (r + 1 < a.Rank) 
       ClearArray(a, r + 1, indices, v); 
      else 
       a.SetValue(v, indices); 
     } 
    } 
+0

अच्छा! छोटा और साफ एक जादू की तरह काम करता है। – vidstige

6

आप एक निर्माण कर सकते हैं भागों के एक समूह से बाहर समाधान। समाधान का स्केच है: अनुक्रमों का एक गुच्छा शून्य से लंबाई -1 तक, सरणी के प्रत्येक आयाम के लिए एक अनुक्रम बनाएं। फिर उन अनुक्रमों के कार्टेशियन उत्पाद लें। यह आपको इंडेक्स का अनुक्रम देता है। उत्पाद के साथ

आइए शुरू:

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
    this IEnumerable<IEnumerable<T>> sequences) 
{ 
    IEnumerable<IEnumerable<T>> emptyProduct = 
    new[] { Enumerable.Empty<T>() }; 
    return sequences.Aggregate( 
    emptyProduct, 
    (accumulator, sequence) => 
     from accseq in accumulator 
     from item in sequence 
     select accseq.Concat(new[] {item})); 
} 

मैं के बारे में बात कैसे इस कोड को यहाँ काम करता है:

http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx

हम दृश्यों के एक दृश्य की जरूरत है। क्या अनुक्रम?

var sequences = from dimension in Enumerable.Range(0, array.Rank) 
     select Enumerable.Range(array.GetLowerBound(dimension), array.GetLength(dimension)); 

तो हम, के रूप में दृश्यों है कहते हैं:

{ 
    { 0, 1, 2 }, 
    { 0, 1, 2, 3 } 
} 

अब उत्पाद की गणना:

var product = sequences.CartesianProduct(); 

तो उत्पाद

{ 
    { 0, 0 }, 
    { 0, 1 }, 
    { 0, 2 }, 
    { 0, 3 }, 
    { 1, 0 }, 
    { 1, 1 }, 
    { 1, 2 }, 
    { 1, 3 }, 
    { 2, 0 }, 
    { 2, 1 }, 
    { 2, 2 }, 
    { 2, 3 } 
} 

है और अब आप कह सकते हैं

foreach(IEnumerable<int> indices in product) 
    array.SetValue(value, indices.ToArray()); 

क्या यह सब समझ में आता है?

+0

हाँ, यह बहुत समझ में आता है! बहुत शैक्षिक और स्पष्ट रूप से लिखा है। धन्यवाद। – vidstige

+2

सुंदर, लेकिन धीरे-धीरे –

+0

@EricLippert 'का चयन करें एन्यूमेरेबल। रेंज (0, सरणी। गेटअपपरबाउंड (आयाम));' अनन्य। रेंज (0, सरणी। गेटलेथेंथ (आयाम)) का चयन करना चाहिए; 'मुझे विश्वास है। आकार के एक आयामी सरणी के मामले पर विचार करें 1. ऊपरीबाउंड 0 होगा, रेंज 0 मान वापस कर देगी, जब इसे वापस करना चाहिए 1. रेंज एक गिनती लेती है, न कि जाने के लिए मूल्य। – Servy

संबंधित मुद्दे