web-dev-qa-db-ja.com

このデータを保存する最も効率的な方法は何ですか?

私は古いVB=コードの書き換えを担当しています。どのように機能するかは理解していますが、彼らがやったことを実行するはるかに効率的な方法があるように感じます。これは、データ要件の点で、私がする必要があるものと本当に似ている不自然な例です。

ユーザーは、GUIで車のメーカー、メーカー、モデル、色を選択する必要があります。次のような大きなテキストファイルがあります。

Ford Truck F150 red
Ford Truck F150 blue
Ford Truck F150 black
Ford Truck F150 silver
Ford Truck F250 red
Ford Truck F250 green
Ford Sedan Taurus red
Ford Sedan Taurus green
Ford Sedan Taurus white
Ford...
...

Subaru SUV Forester blue
Subaru SUV Forester red
Subaru SUV Outback Black
Subaru SUV Outback Green
Subaru SUV Outback Blue
Subaru SUV Outback Red
Subaru...
...

etc.

したがって、最初の選択がSubaruの場合、2番目のボックス(make)にはnotが必要です。これは、Subaruがトラックではないためです。同様に、彼らがフォード、セダン、トーラスを選択した場合、最後のボックス(色)はnot青を選択するオプションを表示する必要があります。または黒。または、赤、緑、白以外のもの。

私の前にコードを書いた人はこれを思いつきました(python-y psuedocodeで):

def getValidOptions():
    items = []
    for i from 0 to numRows:
        options = getLine().split()
        if selectingManufacturer:
            if options[0] not in items:
                items.append(options[0])
        else if selectingMake:
            if selectedManufacturer == options[0] and options[1] not in items:
               items.append(options[1])
        else if selectingModel:
            if selectedManufacturer == options[0] and selectedMake == options[1] and options[2] not in items:
                items.append(options[2])
        else if selectingColor:
            if selectedManufacturer == options[0] and selectedMake == options[1] and selectedModel == options[2] and options[3] not in items:
                items.append(options[3])
    return items

アルゴリズムレベルでも構文レベルでも、それは恐ろしいだけだと思います。 1つは、正しく実行された場合に数行を読み取るだけでよい場合、ファイル全体を解析します。これをさらに非効率的にするために、私の実際のデータには、4つではなく6つの選択肢があります。これは、データの重複の量を考えると、必要以上のデータを格納することにもなります。

ファイルにデータを保存する別の方法、またはgetValidOptions関数をより美しく効率的にするためにデータを解析する別の方法を探しています。これを行う方法はありますか?

9
James

私が読んだ他のすべての回答は、ソフトウェア開発の2つの非常に基本的なルールを無視しているようです。

  • 最初に要件を明確にします(特にパフォーマンスとストレージの要件)

  • シンプルに、愚かにしてください( [〜#〜] kiss [〜#〜] を参照)

あなたは「テキストファイルは大きい」と書いたが、tooと書いていないので、あなたの直感を除いて、そのサイズに実際には何も問題はないと思います。そのため、ファイルのロードにそれほど時間がかからず、IT部門または他の誰かがディスク容量の浪費について不満を述べておらず、ファイルの維持に関する問題について誰も不満を述べていない場合は、ファイル形式をそのままにしてください-過小評価しないでくださいシンプルさの価値。

アルゴリズムの効率についても不満がありますが、これは実際にはそれほど効率的ではありませんが、非常に大きな利点が1つあります。したがって、十分に効率的である限り、時期尚早の最適化を適用しないでください。

それでは、プログラムが十分に高速に動作すると仮定しましょう。最初に確認する必要があるのは、コードをシンプルにする方法とDRY原則)をどのように改善できるかということです。そして、それは確かに改善すべき点です。現在のコードはDRYではないため。この例では、「上位レベル」のオプションが現在の行と一致する場合、ほぼ同じコードブロックテストが4回表示されます。これにより、同じ種類の「追加」呼び出しが4回発生します(実際のコードでは、あなたが書いたように、繰り返しは少なくとも6回発生します。数値選択レベルを導入するか、すでに選択されているオプションを順序付きリストで渡すことで、簡単にそれを回避できます。疑似コードを使用すると、に沿って何か

# selectedOptions is a list, containing either nothing, or "selectedManufacturer"
# or [selectedManufacturer, selectedMake], ..., and so on
def getValidOptions(selectedOptions):
    items = []
    level = selectedOptions.size()
    for i from 0 to numRows:
        options = getLine().split()
        if selectedOptions == options[0:level-1] and options[level] not in item:
            items.append(options[level])
    return items

したがって、これは基本的に同じアルゴリズムであり、コードを繰り返す必要はありません。

getValidOptionsは複数回(少なくともレベルごとに1回)呼び出す必要があることは明らかなので、最適化を1つだけ適用することをお勧めします(まだそうでない場合):getLine関数は、メインメモリからデータをプルし、ディスクからファイルを何度も読み取りません。

6
Doc Brown

さて、あなたが持っているデータはツリーのような構造をしており、メーカーごとにモデルのツリーがあり、モデルごとに色(など)があります。

したがって、このデータのプロセスを2つのステップで分離できます。

  1. テキストファイルを更新したら、そのファイルを処理してツリー構造に変換する必要があります。
  2. アプリケーションをロードするときは、ツリー構造のみをロードします。

ツリー構造は、いわゆるハッシュ連想配列またはdictionaryJava、PHP、JavaScriptまたはPythonなどの言語。この構造により、以下が得られます。

  • 最初のキーはメーカーです。
  • それらの値は、別のハッシュまたは辞書であり、各キーはモデルです。
  • それらの値は色です。または、キーに3番目のレベルを保持し、値として4番目のレベルを保持する別の構造。
  • 等々...

プログラミング言語に応じて、これはより速くまたは遅く実装できます。例えば:

  • C#:ツリー構造を実装できます1、シリアル化可能としてマークします。
  • VB.Net:1つのアプリケーションでツリー構造を生成し、それをファイルにシリアル化できます。
    • このため、Runtime.Serialization.Formatters.Binary.BinaryFormatterは便利かもしれませんが、VB.Netでのシリアル化の専門家ではありません。
  • Javascript:JSONファイルでツリー構造を生成できます。これは、アプリが読み込まれるたびに読み込まれる必要があります。
  • [〜#〜] php [〜#〜]:ツリーデータ構造のシリアル化されたバージョンを生成するか、JSONをロードすることもできます。
  • Java:そのデータ構造をシリアル化して、インターフェースを実装するClassを作成できますJava.io.Serializable

参照

1: https://dvanderboom.wordpress.com/2008/03/15/treet-implementing-a-non-binary-tree-in-c/
-C#でのツリーの実装に関する完全な説明。
-誰かがそのオブジェクトについてserializingについて尋ねたコメントと、そのコメントの回答を探します。

6
Nicolás

データを保存する簡単な方法の1つは、SQLiteデータベースにデータを格納することです。 SQLiteは、ほとんどのSQLデータベースとは異なり、アプリケーションファイル形式としての使用に適しています。このアプローチにはいくつかの利点があります。

  1. シリアライザやデシリアライザをコーディングする必要はありません。
  2. ファイルは、多数の既存のプログラムによって編集および照会できます。
  3. 質問で言及する条件付きの乱雑さは回避されます。ドロップダウンを制限するには、各列に対して単純なwhere句を生成します(例:select distinct model where manufacturer='ford' and color = 'red')。

これにより、SQLを学習する必要がありますが、カスタムファイル形式を学習する必要がなくなります。

3
Brian

ファイルの行にランダムにアクセスできるため、ファイルをレコードの配列として扱うことができると思います。行にランダムにアクセスできない場合は、使用しているアルゴリズムが最善です。

最も高速なアクセスのために、データを6つのファイルに保存します。各ファイルは次のインデックスです。

フラットファイルインデックスを作成するには、さまざまな方法があります。私は通常、添え字範囲を使用します。ユーザーが選択するたびに、範囲を使用して次のファイルの読み取りを制限します。

ここに、あなたが提供したサンプルデータのインデックスを作成する方法を示します。

もちろん、ファイルはソートする必要があります。説明のために行に番号を付けました。行番号はファイルに表示されません。

--| file3.dat |--
 0 Ford Truck F150 red
 1 Ford Truck F150 blue
 2 Ford Truck F150 black
 3 Ford Truck F150 silver
 4 Ford Truck F250 red
 5 Ford Truck F250 green
 6 Ford Sedan Taurus red
 7 Ford Sedan Taurus green
 8 Ford Sedan Taurus white
 9 Subaru SUV Forester blue
10 Subaru SUV Forester red
11 Subaru SUV Outback Black
12 Subaru SUV Outback Green
13 Subaru SUV Outback Blue
14 Subaru SUV Outback Red

最初のインデックスを作成するには、ファイルの最初の3つのフィールドの一意の組み合わせごとにレコードを作成します。各レコードに、その組み合わせが表示される最初と最後の行番号を格納します。

--| file2.dat |--
 0 Ford Truck F150       0   3
 1 Ford Truck F250       4   5
 2 Ford Sedan Taurus     6   8
 3 Subaru SUV Forester   9  10
 4 Subaru SUV Outback   11  14

2番目のインデックスは、最初のインデックスの最初の2つのフィールドを使用して、同様に構築されます。

--| file1.dat |--
 0 Ford Truck        0   1
 1 Ford Sedan        2   2
 2 Subaru SUV        3   4

そして3番目は、この場合の最上位レベルのインデックスです。

--| file0.dat |--
 0 Ford          0   1
 1 Subaru        2   2

概念を説明しすぎだと思いますが、一般的には、最後のフィールドを削除して重複レコードを排除することにより、インデックスを作成します。

一部の冗長データを排除することにより、ファイルストレージ要件をさらに削減できます。

たとえば、各インデックスレコードの「最初の」添え字は、常に前のレコードの「最後の」添え字より1つ多いか、前のレコードがない場合はゼロです。したがって、「最初の」添え字を格納する必要はありません。説明のために下に残しておきます。

また、各レコードの最後のフィールドのみを使用して選択リストに入力するため、他のフィールドを格納する必要はありません。

インデックスカスケードの最小レンディションは、次のようになります。ティック 'は、実際にはファイルに保存されていない数値を示します。

--| file0.dat |--
 0' Ford         0'   1
 1' Subaru       2'   2

--| file1.dat |--
 0' Truck        0'   1
 1' Sedan        2'   2
 2' SUV          3'   4

--| file2.dat |--
 0' F150         0'   3
 1' F250         4'   5
 2' Taurus       6'   8
 3' Forester     9'  10
 4' Outback     11'  14

--| file3.dat |--
 0' red
 1' blue
 2' black
 3' silver
 4' red
 5' green
 6' red
 7' green
 8' white
 9' blue
10' red
11' Black
12' Green
13' Blue
14' Red

インデックスからの選択リストに入力するときは、前のインデックスでユーザーが選択した「最初」と「最後」の添え字を使用して、読み取られる行を制限します。

例:

すべてのfile0.datから最初の選択リストに入力します。 (フォード、スバル)

ユーザーは「フォード」を選択します。対応する添え字は0と1です。

file1.datの0〜1行目の2番目の選択リストに入力します。 (トラック、セダン)

ユーザーは「セダン」を選択します。対応する添え字は2と2です。

ご覧のように、ユーザーが「フォード」「セダン」「トーラス」などを選択するまでに、file3.datの6行目から8行目までを読み取るだけで、4番目の選択リストを満たす必要があることがわかります。

説明が長すぎて申し訳ありませんが、ここでは非常に遅く、短い説明を書く時間はありません。

追加:さらに考えて、ファイルを1つに連結することができます。

--| file.dat |--
 0' -            1'   2
 1' Ford         3'   4
 2' Subaru       5'   5
 3' Truck        6'   7
 4' Sedan        8'   8
 5' SUV          9'  10
 6' F150        11'  14
 7' F250        15'  16
 8' Taurus      17'  19
 9' Forester    20'  21
10' Outback     22'  25
11' red          -'   -
12' blue         -'   -
13' black        -'   -
14' silver       -'   -
15' red          -'   -
16' green        -'   -
17' red          -'   -
18' green        -'   -
19' white        -'   -
20' blue         -'   -
21' red          -'   -
22' Black        -'   -
23' Green        -'   -
24' Blue         -'   -
25' Red          -'   -

これは、複数のファイルバージョンとまったく同じように使用されますが、最初の添え字範囲を含めるにはダミーの最初の行が必要です。

1
A. I. Breveleri